What each tier promises
const url = 'https://api.hyperresearch.ai/v1/tiers';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.hyperresearch.ai/v1/tiersUnauthenticated. Lists exactly the tiers POST /v1/runs will accept, in ladder order, each with the display_name to print, the purpose line to show on its card and a recommended flag (false on every tier while the ladder is Light and Deep). The ENFORCED numbers are the pipeline’s own: sources.target_high is the fetch budget the width sweep enforces, and duration_estimate is the research profile’s estimate. What a card should PRINT is served beside them: duration_display, sources_display (from sources.typical_low–typical_high), words.typical_min–typical_max and description, which carry a measured envelope where one exists (Light, 2026-09-23) and otherwise repeat the enforced numbers. A client that renders a tier card must read it from here rather than hardcoding a band, which is how a Light run came to report 66 sources against an advertised 15–25 (E2E4-16).
Responses
Section titled “ Responses ”Tiers
object
object
The tier id, which is what POST /v1/runs takes and what every stored run, receipt and plan hash carries. Never shown to a person: use display_name.
What this tier is called in customer-facing text. Not always the id title-cased — premier is shown as “Deep”. Anything that prints a tier word to a person must print this.
One line saying what this tier is FOR: the copy on its card.
True for a tier a client should show as a highlighted default. While the ladder is two rungs (Light and Deep) NO tier is marked and this is false on every row: a badge between one cheap tier and one deep one is a thumb on a scale the customer can read for themselves. The field stays so the schema does not change when a badge comes back. A client pre-selects the cheapest listed tier, not the recommended one.
List price for one run at this tier.
object
The floor the pipeline gates on: a run that cannot reach it does not proceed to drafting.
The number the fetch budget is set to. budget.sources_max defaults to exactly this, and the width sweep stops here — so the advertised ceiling IS the enforced one (E2E4-16).
The highest budget.sources_max this API will accept from a caller who names one. Always >= target_high; a caller who names nothing gets target_high.
The low end of how many sources a finished run at this tier typically reads — what a card should print. Display only: it can exceed target_high, because later stages (claim check, source lanes) read past the width sweep. Equal to target_low when the tier has no measured envelope.
The high end of typical_low’s band. Equal to target_high when the tier has no measured envelope.
The source band as a card prints it, e.g. “about 20–50 sources”. Built from typical_low–typical_high.
The research profile’s own estimate, which is part of every run’s frozen plan. For what to PRINT, read duration_display.
How long a run takes, as a card prints it. The measured envelope where one exists (Light: “about 30–40 min”), otherwise duration_estimate.
Pipeline steps this tier executes.
True when this tier cannot be started by a PAYG account with no card on file. Deep commits hours of research and over a hundred sources before there is a report to show for it, so it is card-required; an invoiced Enterprise plan satisfies the same gate. A client that renders a tier card must read this rather than hardcoding the tier names, and must say what to do about it — the API’s own refusal does.
True when an included run can ever be claimed at this tier. Two tiers can: Deep, which a first card attach grants one of, and Light, which accounts created before 2026-10-15 were given two of with no card (a new account gets no included Light run, so its Light runs are charged from the first). A Deep allowance is GRANTED rather than stamped at sign-up, so trial_eligible being true does not mean this account has one — it means the column exists to be spent. Every other tier is charged in full. This is not the opposite of requires_card: a free Deep needs a card on file to start, because the card is what earned it.
How many chapters a run at this tier is partitioned into, or null when the report is written as one piece. A chaptered run researches each chapter separately and then writes the report section by section, which is why it takes hours rather than minutes.
object
min/max: the length band the ship gate measures the finished report against. typical_min/typical_max: what a finished report typically runs to, which is what to print.
object
The low end of how long a finished report at this tier typically runs, in words — what a card should print. Equal to min when the tier has no measured envelope.
The high end of typical_min’s band.
One sentence describing the tier. The measured display copy where one exists, otherwise the research profile’s own description.
Example
{ "data": [ { "display_name": "Deep", "purpose": "Investigate a broader question across several research sections", "sources_display": "about 20–50 sources", "duration_display": "about 30–40 min" } ]}Too many requests from this network; wait Retry-After seconds
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}Headers
Section titled “Headers”Seconds to wait before retrying. Always present on a 429.
Example
"reads";q=60;w=60The quota policy this request was counted against (draft-ietf-httpapi-ratelimit-headers): "<bucket>";q=<requests>;w=<window seconds>, e.g. "reads";q=60;w=60. Buckets: reads 60/min, status 600/min, creates 10/min, writes 20/min per credential; public 300/min per network on keyless routes.
Example
"reads";r=57;t=41Remaining quota in the current window: "<bucket>";r=<remaining>;t=<seconds until reset>, e.g. "reads";r=57;t=41.
Requests allowed per window in this bucket (legacy form of RateLimit-Policy).
Requests left in this window (legacy form of RateLimit).
Unix time (seconds) when the window resets.
Which bucket counted the request: reads, status, creates, writes or public.
Internal error
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}