HonestHook

RankingsSign in

Home · Docs · Recipes · Whether a spike held or decayed within six hours

Whether a spike held or decayed within six hours

Two 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, using 3 endpoints. Step 1 is free and needs no key.

Read these first — they cost money if you do not

4 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`.

`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`.

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.


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 six-hour window

`hours=6` is a target, not a guarantee. The archive answers with the closest window it actually holds and reports the real gap, so a six-hour question can come back compared against a window four or nine hours old.

$ curl -H "Authorization: Bearer hk_live_..." \
     "https://honesthook.com/api/v1/movers/indie-saas?hours=6"
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: If `interval_min` comes back as 320 you did not measure six hours, you measured five and a third. Decay per hour means `points_gained / (interval_min / 60)` — never divide by the 6 you sent.

3. Read the series, which is where decay is actually visible

`/movers` only ever reports items that GAINED between the two windows it compared — the schema says so. An item that spiked and then fell may not appear there at all, which means the endpoint that answers 'did it climb' cannot answer 'did it hold'. For that you need the hourly readings: each `SeriesPoint` carries `window`, `position`, `points` and `comments`, one per item per hour.

$ curl -H "Authorization: Bearer hk_live_..." \
     "https://honesthook.com/api/v1/trends/indie-saas?from=2026-10-01T18:00:00Z&to=2026-10-02T06: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: Compare `points` across consecutive `window` values on the same item. Points are monotonic upstream, so a spike that held shows a flat tail and one that decayed shows the rate of gain collapsing — the count itself does not fall.

4. Read `window`, not the order the points arrive in

`window` is rounded to the hour and is the bucket, not the exact capture time — two runs in the same hour share one. Captures are not equidistant, so the gap between consecutive points is not a constant hour. Read the timestamps; do not assume the spacing.

Watch for: This is the same mistake as dividing by the `hours` you asked for, one level down: a delta without its real interval is a number without a unit.


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