Skip to content

Errors and retries

Retry GET, HEAD and OPTIONS after network failures, 408, 429 and temporary 5xx responses. Respect Retry-After.

Retry a write only with the same idempotency key and unchanged body. A new question or tier needs a new key.

Every 4xx and 5xx response has the same JSON body:

{
"error": {
"type": "rate_limited",
"code": "rate_limit_exceeded",
"message": "Rate limit of 60 reads requests per minute exceeded for this key. Retry in 37s.",
"retry_after": 37,
"retryable": true,
"request_id": "req_01J9ZQ7V6H0000000000000000"
}
}

code is stable and is what a client should branch on; message is for people and can change. retryable says whether the same request may succeed if sent again unchanged, after retry_after when there is one: true for 429, 500, 502, 503, 504 and 409 idempotency_in_flight, false for every other 4xx and for 501. param names the offending field when there is one. Quote request_id (also sent as the X-Request-Id header) when you contact support. The full list of codes is the ErrorEnvelope schema in the API reference.

401 means authentication failed; its WWW-Authenticate header names the realm and, per RFC 9728, the resource metadata at https://api.hyperresearch.ai/.well-known/oauth-protected-resource. 403 means scope, CSRF or policy is insufficient. 409 reports a state conflict. 429 means a rate limit; wait Retry-After seconds.

The Answers routes can also answer 503, always with Retry-After. A 503 is free, with one exception: if the billing store fails right after a call was charged, the 503 says so, and retrying with the same Idempotency-Key returns that call’s result without charging again.

  • answers_capacity: Answers is at capacity. When the day’s capacity is used up, Retry-After runs to 00:00 UTC and error.resets_at says when.
  • temporarily_unavailable: the billing store did not answer. Retry after the delay, with the same Idempotency-Key if you sent one.
  • engine_unavailable (search, on deployments that report search failures over HTTP): the search did not finish. Otherwise the same failure is a 200 with status: "error" and an error object whose retryable is true.

Limits are per API key, per minute:

Bucket Limit What counts
status 600 Polling one run: GET /v1/runs/{id} and its event stream
reads 60 Everything not listed elsewhere
writes 20 Creating API keys and webhooks
creates 10 Starting billable work: runs, re-runs, verification, fetches, vault chat, exports, webhook tests
ask 60 Answers calls: POST /v1/answers and POST /v1/answers/search

Creates, writes and Answers calls also have a per-workspace ceiling (40, 60 and 120 a minute), so more keys do not buy more throughput. Reading a stored Answers call (GET /v1/answers and GET /v1/answers/{id}) counts against reads. Keyless routes are limited to 300 requests a minute per network.

Windows are fixed 60-second windows. A refusal from the per-workspace ceiling also carries X-RateLimit-Scope: workspace, and its RateLimit-Policy names ask-workspace (or creates-workspace, writes-workspace). Separately from the per-minute limit, an account can have only a few Answers calls running at once; one over that answers 429 concurrency_limit with Retry-After: 5 and lists the calls still running in error.in_flight, so you can collect those with GET /v1/answers/{id} instead of asking again.

Responses carry the limit that applied, in two forms:

  • RateLimit-Policy: "reads";q=60;w=60 and RateLimit: "reads";r=57;t=41 (IETF RateLimit fields): quota q per window w seconds, r requests remaining, t seconds until the window resets;
  • X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix seconds) and X-RateLimit-Bucket.

A 429 always carries Retry-After and the same number in error.retry_after. Refused requests do not count against the limit, so a client that waits as told recovers on schedule.

How /v1 changes and how a deprecation is announced: Versioning and deprecation.