HonestHook

RankingsSign in

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"}]}
Real response, HTTP 200, captured for this page.

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"
No response body here. The 200 body for this call is NOT transcribed on this page. Reading it 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. Rather than show you a plausible example we never received, we show the field-by-field shape from the contract and link the schema page. The shape is guarded against the live contract by a test; an invented example would be guarded by nothing.

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 401
Real response, HTTP 401, captured for this page. Note the extra `http` field, which the `/trends` refusal above does not carry. That difference is real and documented: this refusal originates inside the database, which echoes the status in the body, while the other refuses before reaching it.

4. 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"
No response body here. The 200 body for this call is NOT transcribed on this page. Reading it 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. Rather than show you a plausible example we never received, we show the field-by-field shape from the contract and link the schema page. The shape is guarded against the live contract by a test; an invented example would be guarded by nothing.

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:

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

← All recipes · Schemas · Endpoint reference · Full docs