Skip to content

What each tier promises

GET
/v1/tiers
curl --request GET \
--url https://api.hyperresearch.ai/v1/tiers

Unauthenticated. 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).

Tiers

Media typeapplication/json
object
data
required
Array<object>
object
tier
required

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.

string
display_name
required

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.

string
purpose
required

One line saying what this tier is FOR: the copy on its card.

string
recommended
required

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.

boolean
price_cents
required

List price for one run at this tier.

integer
sources
required
object
min
required

The floor the pipeline gates on: a run that cannot reach it does not proceed to drafting.

integer
target_low
required
integer
target_high
required

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

integer
max_requestable
required

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.

integer
typical_low
required

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.

integer
typical_high
required

The high end of typical_low’s band. Equal to target_high when the tier has no measured envelope.

integer
sources_display
required

The source band as a card prints it, e.g. “about 20–50 sources”. Built from typical_low–typical_high.

string
duration_estimate
required

The research profile’s own estimate, which is part of every run’s frozen plan. For what to PRINT, read duration_display.

string
duration_display
required

How long a run takes, as a card prints it. The measured envelope where one exists (Light: “about 30–40 min”), otherwise duration_estimate.

string
steps
required

Pipeline steps this tier executes.

integer
requires_card
required

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.

boolean
trial_eligible
required

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.

boolean
chapters
required

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
required
integer
max
required
integer
words
required

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
min
required
integer
max
required
integer
typical_min
required

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.

integer
typical_max
required

The high end of typical_min’s band.

integer
description
required

One sentence describing the tier. The measured display copy where one exists, otherwise the research profile’s own description.

string
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

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: invalid_request authentication permission not_found rate_limited quota_exceeded budget_blocked conflict provider_unavailable internal
code
required
string
Allowed values: missing_api_key malformed_api_key invalid_api_key api_key_expired api_key_revoked account_suspended workspace_unavailable session_expired insufficient_scope signup_closed role_not_permitted membership_required csrf_required csrf_invalid origin_not_allowed https_required key_route_restricted tier_not_allowed runs_paused invalid_body invalid_query invalid_path query_too_short query_not_research query_too_long query_refused invalid_title invalid_brief invalid_must_read invalid_research_brief invalid_time_window invalid_pulse invalid_metadata invalid_tier too_many_required_headings sources_max_above_ceiling reuse_scope_needs_project invalid_webhook_url metadata_too_large invalid_settings invalid_step unsupported_tier tier_not_offered plan_not_offered provider_excluded dry_run_not_enabled invalid_dry_run_fault chaos_faults_not_enabled invalid_chaos_fault gone run_not_found key_not_found workspace_not_found webhook_not_found artifact_not_found note_not_found escalation_not_found source_not_found connection_not_found project_not_found answer_not_found account_not_found feature_not_enabled idempotency_key_reuse idempotency_in_flight run_not_resumable run_not_finish_resumable run_finish_resume_exhausted run_recovering rerun_not_platform platform_rerun_taken run_not_clarifying result_not_ready workspace_not_empty turnstile_invalid turnstile_replayed demo_daily_limit demo_receipt_invalid demo_disabled user_not_found member_not_found member_exists last_owner cannot_self_elevate workspace_limit invitation_not_found invitation_expired invitation_email_mismatch account_mismatch ip_not_allowed chat_keys_retired project_name_taken project_archived run_not_filable confirmation_mismatch ownership_transfer_required account_deleting export_cooldown migration_pending rate_limit_exceeded concurrency_limit spend_cap_exceeded trial_exhausted payment_method_required payment_disputed card_hold_unavailable card_required insufficient_balance wallet_frozen topup_amount_invalid topup_daily_limit answers_capacity platform_capacity daily_run_limit internal_budget orchestrator_unavailable source_unconfigured source_rate_limited source_upstream_unavailable deployment_misconfigured not_implemented pdf_unavailable internal_error
message
required
string
param
string
retry_after
integer
request_id
required
string
Example
{
"error": {
"type": "invalid_request",
"code": "missing_api_key",
"request_id": "req_01J9ZQ7V6H0000000000000000"
}
}
Retry-After
integer

Seconds to wait before retrying. Always present on a 429.

RateLimit-Policy
string
Example
"reads";q=60;w=60

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

RateLimit
string
Example
"reads";r=57;t=41

Remaining quota in the current window: "<bucket>";r=<remaining>;t=<seconds until reset>, e.g. "reads";r=57;t=41.

X-RateLimit-Limit
integer

Requests allowed per window in this bucket (legacy form of RateLimit-Policy).

X-RateLimit-Remaining
integer

Requests left in this window (legacy form of RateLimit).

X-RateLimit-Reset
integer

Unix time (seconds) when the window resets.

X-RateLimit-Bucket
string

Which bucket counted the request: reads, status, creates, writes or public.

Internal error

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: invalid_request authentication permission not_found rate_limited quota_exceeded budget_blocked conflict provider_unavailable internal
code
required
string
Allowed values: missing_api_key malformed_api_key invalid_api_key api_key_expired api_key_revoked account_suspended workspace_unavailable session_expired insufficient_scope signup_closed role_not_permitted membership_required csrf_required csrf_invalid origin_not_allowed https_required key_route_restricted tier_not_allowed runs_paused invalid_body invalid_query invalid_path query_too_short query_not_research query_too_long query_refused invalid_title invalid_brief invalid_must_read invalid_research_brief invalid_time_window invalid_pulse invalid_metadata invalid_tier too_many_required_headings sources_max_above_ceiling reuse_scope_needs_project invalid_webhook_url metadata_too_large invalid_settings invalid_step unsupported_tier tier_not_offered plan_not_offered provider_excluded dry_run_not_enabled invalid_dry_run_fault chaos_faults_not_enabled invalid_chaos_fault gone run_not_found key_not_found workspace_not_found webhook_not_found artifact_not_found note_not_found escalation_not_found source_not_found connection_not_found project_not_found answer_not_found account_not_found feature_not_enabled idempotency_key_reuse idempotency_in_flight run_not_resumable run_not_finish_resumable run_finish_resume_exhausted run_recovering rerun_not_platform platform_rerun_taken run_not_clarifying result_not_ready workspace_not_empty turnstile_invalid turnstile_replayed demo_daily_limit demo_receipt_invalid demo_disabled user_not_found member_not_found member_exists last_owner cannot_self_elevate workspace_limit invitation_not_found invitation_expired invitation_email_mismatch account_mismatch ip_not_allowed chat_keys_retired project_name_taken project_archived run_not_filable confirmation_mismatch ownership_transfer_required account_deleting export_cooldown migration_pending rate_limit_exceeded concurrency_limit spend_cap_exceeded trial_exhausted payment_method_required payment_disputed card_hold_unavailable card_required insufficient_balance wallet_frozen topup_amount_invalid topup_daily_limit answers_capacity platform_capacity daily_run_limit internal_budget orchestrator_unavailable source_unconfigured source_rate_limited source_upstream_unavailable deployment_misconfigured not_implemented pdf_unavailable internal_error
message
required
string
param
string
retry_after
integer
request_id
required
string
Example
{
"error": {
"type": "invalid_request",
"code": "missing_api_key",
"request_id": "req_01J9ZQ7V6H0000000000000000"
}
}