API recipes
Three questions the HonestHook archive can answer, each with the calls that answer it and the measured caveats that change the result.
3 recipes, 12 steps between them, and 7 distinct measured caveats. These are not example use cases chosen to fill a page — they are the three questions the product already claims the archive can answer, and the list is closed for that reason.
The 3 recipes
- Which topics in your niche climbed fastest this weekRank a week of archived history by how much each item actually gained, not by where it sits now. Three calls, one of them free. — 4 steps, 5 caveats.
- What was trending the day a competitor launchedPoint `from` and `to` at one past day and read back the ranking as the archive recorded it. This is the question no live API can answer. — 4 steps, 3 caveats.
- Whether a spike held or decayed within six hoursTwo reads of the same item six hours apart, compared on points — with the trap that makes position look like movement when nothing moved. — 4 steps, 4 caveats.
All three start with the same free call
Nothing downstream validates a niche name. A typo against /trends or /movers does not return 404 — it returns an empty list, HTTP 200, and spends the call anyway, because the quota counter increments before the archive is read. So every recipe here starts by resolving names against an endpoint that is free and needs no key:
$ curl -s "https://honesthook.com/api/v1/nichos"
{"niches":[{"id":"ai-agents","label":"AI agents","windows_24h":24,"last_window":"2026-10-02T09:00:00+00:00"},{"id":"devtools","label":"Developer tools","windows_24h":24,"last_window":"2026-10-02T09:00:00+00:00"},{"id":"indie-saas","label":"Indie SaaS","windows_24h":24,"last_window":"2026-10-02T09:00:00+00:00"}]}Take the id values from that response and use them verbatim. windows_24h tells you how many hourly windows landed in the last day, which is a coverage statement — a niche with gaps will give you a flat series that looks like a quiet market.
The caveats, in one place
Each recipe repeats the ones that apply to it, before its steps rather than after. Collected here so you can read them once:
- A nonexistent niche is not a 404 — and it spends the call — Nothing validates the niche name. A typo returns `items: []` with `points: 0` and HTTP 200, AND still spends a call, because the quota counter increments before the archive is read. An empty answer therefore means one of two different things — the niche is quiet, or the niche does not exist — and the response does not distinguish them. This is why step 1 of every recipe here resolves names at `/api/v1/nichos` first: that endpoint is free and needs no key, so the check costs nothing.
- Position changes without points changing — Measured on 2026-09-06 across 19 pairs of windows: position changed for 19 of 29 items WITHOUT A SINGLE POINT CHANGING. Most items in a snapshot sit within a few points of each other, so ties are the rule and a tie reshuffle looks exactly like movement if you read `position`. That is why `/movers` ranks by `points_gained` — an upstream counter — and carries `position` for reference only.
- The ranges /movers enforces — `hours` accepts 1 to 720 and defaults to 24. `limit` accepts 1 to 100 and defaults to 20. Out of range is an explicit 400, never silently clamped on the way in — but `limit` IS clamped to 100 inside the database, so a larger value does not raise the cap.
- `interval_min` is the real gap, not the one you asked for — `/movers` compares the newest window against the archived window whose real capture time is closest to `hours` ago. Windows are not equidistant — observed gaps have ranged from 12 to 321 minutes — so asking `hours=24` against an archive 20 hours deep answers `interval_min: 1208`, not 1440. Divide `points_gained` by `interval_min`, never by the number you sent.
- An empty /movers result is a 200 and is not charged — `/movers` charges only if it finds movement. A quiet window answers HTTP 200 with `credits_charged: 0`, `items: []` and a `note` saying so — not an error and not a charge. The risk of an empty window sits with us, so you do not need defensive logic to avoid calling. Branch on `total`, not on the status code.
- An unknown parameter is ignored in silence — `/trends` declares exactly four inputs: `niche`, `from`, `to` and `limit`. Anything else is not rejected — it is ignored. A `?platform=hackernews` produces no error, no warning and no filtering: you get the unfiltered answer and nothing tells you the filter did not apply. Check the parameter against the list before assuming a narrower result.
- The series is capped, and says when it was cut — `/trends` is capped at 5000 series points and a 90-day span, both enforced in the database. When a cap is hit, `truncated` is true and the series is incomplete — narrow the interval rather than drawing conclusions from a partial series. `limit` is a hint only: the hard cap lives in the database and a larger value does not raise it.
What these pages do not show
No 200 body for a call that needs a key is transcribed anywhere in this family. Reading one needs a client key, and there is no client key on the machine these pages were built from — internal keys exist only as hashes, by design. The responses you see here are real ones we could actually make: the free /nichos call above and two refusals. Everything else is described by its schema, which is generated from the contract and tested against it, rather than by an example nobody received.
Next
Response schemas · Endpoint reference · Full docs · Archive status