Home · Docs · Recipes · Which topics in your niche climbed fastest this week
Which topics in your niche climbed fastest this week
Rank 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, using 3 endpoints. Step 1 is free and needs no key.
Read these first — they cost money if you do not
5 measured caveats apply to this recipe. They are worth more than the steps below: each one is a way the API answers plausibly while meaning something other than what you assumed.
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.
Source: The `/api/v1/trends/{niche}` description in the served `/openapi.json`.
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.
Source: The `/api/v1/movers/{niche}` description in the served `/openapi.json`.
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.
Source: The `/api/v1/movers/{niche}` parameter schemas in the served `/openapi.json`, transcribed 2 Oct 2026.
`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.
Source: The `/api/v1/movers/{niche}` description in the served `/openapi.json`.
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.
Source: The `/api/v1/movers/{niche}` description in the served `/openapi.json`, and `cobra_sem_resultado` on its catalogue row.
The 4 steps
1. Resolve the niche name first — it is free, and skipping it costs
Nothing validates a niche name downstream, so a typo answers 200 with an empty list and still spends a call. This endpoint needs no key and costs no credits, so there is never a reason to guess. Take the `id` values from the response and use those verbatim.
$ 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"}]}2. Ask /movers for a week-wide window
A week is 168 hours, which is inside the 1–720 range `hours` accepts. This ranks by `points_gained` — the upstream counter — which is the whole reason this endpoint answers the question and a ranking by `position` would not.
$ curl -H "Authorization: Bearer hk_live_..." \
"https://honesthook.com/api/v1/movers/ai-agents?hours=168&limit=20"Watch for: Read `interval_min` from the response before you read any `points_gained`. It is the real elapsed time between the two captures compared, and it is routinely not the number you asked for.
3. See what /movers answers without a valid key
Worth running once so you recognise the shape. This is a real refusal, transcribed — note the `http` field, which the `/trends` refusal does not carry.
$ curl "https://honesthook.com/api/v1/movers/ai-agents"
{"error":"chave_invalida","http":401}
HTTP 4014. Pull the series behind the winners
`/movers` tells you what gained; `/trends` tells you the shape of the gain. Match on `external_id` and read each item's `series` — a jump between two readings and a steady climb across the week are different stories that `points_gained` alone cannot tell apart.
$ curl -H "Authorization: Bearer hk_live_..." \
"https://honesthook.com/api/v1/trends/ai-agents?from=2026-09-25T00:00:00Z&to=2026-10-02T00:00:00Z"Watch for: Check `truncated` in the response. If it is true the series was cut by the 5000-point cap and you are looking at a partial week.
The shapes that come back
This recipe touches 4 response schemas. Each is documented field by field, generated from the contract and tested against it:
Mover— What `archive/movers` returns. The ranking key is `points_gained`, not `points` and not `position` — the question is what accelerated, not what is big.Item— What `archive/trends` returns: an item, when it entered the ranking, when it left, its best position, and the full series of hourly readings behind it.SeriesPoint— A single reading in an item's series: where it sat, how many points it had, how many comments. The archive keeps one of these per item per hour and never deletes them.ApiError— Every 4xx and 5xx from the API carries this object. The `error` value is a stable string you can branch on; the extra fields appear only for the errors that have something to report.
The endpoints used
What this page does not show you
The 200 bodies for the calls that need a key are not transcribed anywhere on this page, and that is deliberate rather than unfinished. Reading them needs a client key, there is no client key on the machine these pages were built from, and an example we never received would be indistinguishable from one we did — to you, and eventually to us. The field-by-field shape is on the schema pages above and is checked against the live contract by a test; an invented example would be checked by nothing.
The other 2 recipes
- 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.
- 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.