Rate Limits & Headers

Onlist returns a small set of custom response headers and uses standard HTTP status codes for back-pressure. Everything you need to correlate a request, react to ignored fields, or back off arrives on the response itself. Anything you want to read before sending a request — remaining spend, what a past call cost, daily totals — is a separate call to the OpenRouter-compatible Management API.

Response headers

Onlist adds a handful of headers on top of the usual OpenAI-compatible response. None of them are required to make a request work, but each is useful for observability and correct retry behavior.

HeaderEmitted onDescription
X-Oneapi-Request-IdEvery responseThe request id. This is the value you pass to GET /api/v1/generation to look up what the call cost.
X-Onlist-Route-IdEvery relayed response (stream and non-stream)Opaque UUID for the routing decision — which provider and listing were selected. Not the id you look a call up by; see below.
X-Onlist-ProviderNon-streaming relayed responsesSlug of the provider that actually served the request, written after settlement. Absent on streams: see below.
X-Onlist-AttemptsNon-streaming relayed responsesHow many providers were tried. 1 means the first pick succeeded; higher means failover fired.
X-Onlist-WarningsResponses where a field was ignored or a notice firedComma-and-space-joined, de-duplicated list of accepted-but-ignored provider/request fields plus routing notices.
Retry-After429 (rate limited) and 503 (cooldown)Seconds to wait before retrying. Standard HTTP semantics.

X-Onlist-Route-Id

X-Onlist-Route-Id is an opaque UUID that identifies the routing decision Onlist made for your request. It is written on both streaming and non-streaming responses; on a stream, it is set before the first SSE byte, so it is available as soon as headers arrive.

This is not the response body id field. The body id is the upstream completion id (issued by whichever provider served you); X-Onlist-Route-Id is Onlist's own identifier for which provider and listing were selected.

It is also not the id you use to look a call up programmatically. That is X-Oneapi-Request-Id, which GET /api/v1/generation takes. Three ids travel with one request and they answer different questions:

IdIdentifiesUse it to
body idthe upstream completioncorrelate with the provider's own records
X-Onlist-Route-Idthe routing decisioninspect routing in Usage Logs (signed-in)
X-Oneapi-Request-Idthe request as Onlist recorded itGET /api/v1/generation?id=... — cost, provider, token counts

To inspect the routing decision later, open that call in Usage Logs in the dashboard — the route id identifies the record.

Route inspection is a signed-in surface rather than a programmatic one: it is served to your browser session, and API keys do not authenticate against it. Per-call cost is programmatic, but it is keyed by request id rather than route id.

The route record covers routing only; it carries no cost. Per-request cost lives in the usage log, which also names the provider that served the request, so there is nothing to correlate: read it with GET /api/v1/generation for a single call or GET /api/log/token for a key's recent history. See Usage & cost.

X-Onlist-Provider

X-Onlist-Provider names the provider that actually served the request, which is not necessarily the one first selected: if failover fired, this is the one that finished. X-Onlist-Attempts alongside it tells you how many were tried.

Both are written after settlement, so they only appear on non-streaming responses. On a stream, HTTP headers are committed before the first SSE byte, which is before Onlist knows which provider completes the call, so there is nothing truthful to put there.

That makes these headers a convenience for non-streaming traffic rather than a general mechanism. If you need provider attribution across streaming and non-streaming alike, and especially if you need per-request cost, read it from the usage log instead: every call lands there with its provider and its charge on the same row. See Usage & cost.

X-Onlist-Warnings

Onlist accepts the full OpenRouter-style provider object, but fields it does not act on are silently ignored rather than rejected: you get a 200, not a 400. When that happens, the ignored fields are reported in X-Onlist-Warnings so the behavior is never invisible.

The header value is a single string: a comma-and-space (, ) joined, de-duplicated list of entries. Each entry is either:

  • an ignored: <field> marker, e.g. ignored: provider.sort or ignored: provider.only[1:3];
  • a dropped model variant suffix, in the same ignored: form, e.g. ignored: model variant slug ":online" (not available on Onlist), or ignored: provider.sort due to model variant slug when a :floor/:nitro suffix overrode the body field; or
  • a routing notice, e.g. cool_pool_exhausted_fallback_used.
X-Onlist-Warnings: ignored: provider.sort, ignored: provider.quantizations, ignored: provider.only[1:3]

The notices that can appear, as opposed to the open-ended ignored: markers, are a closed set:

NoticeMeaning
cool_pool_exhausted_fallback_usedEvery provider matching your constraints was in cooldown, so one of them was tried anyway instead of failing the request.
allow_pool_exhausted_fallback_usedYour provider.allow list was exhausted and routing fell through to the rest of the pool.
order_exhausted_fallback_usedYour provider.order list was exhausted and routing fell through to the rest of the pool.
allow_ignore_coexist_not_in_uiprovider.allow and provider.ignore were both non-empty. Honored as allow minus ignore; the dashboard cannot express this.
order_without_allow_not_in_uiprovider.order was set with an empty effective allow list. Honored; the dashboard cannot express this.
autorouter: model "x" rewritten to "y"An auto sentinel resolved to the static platform-default model. See Models.
claude_code_quota_probe_interceptedA Claude Code quota probe was answered locally instead of relayed. See below.

Claude Code quota probes

Claude Code periodically sends a non-streaming POST /v1/messages probe (max_tokens: 1, a single quota message, no system prompt) to read Anthropic's subscription usage headers. Through Onlist that probe has nothing to measure: any quota it could report belongs to the provider's own account, not to your Onlist balance.

Onlist answers the probe locally with a minimal message, tags the response claude_code_quota_probe_intercepted, and does not relay, bill, or log it. Claude Code reads the header-less 200 as "allowed" and shows no usage figures, the same behavior it has through OpenRouter. Ordinary Claude Code requests are untouched.

The header is present only when something was ignored or a notice fired: a clean request returns no X-Onlist-Warnings header at all. Parse it by splitting on the literal string , (see Reading the warnings header below). For the full list of which provider fields are honored versus ignored, and which model suffixes are routing shortcuts rather than separate models, see Provider routing.

Caution

X-RateLimit-* headers Unlike some gateways, Onlist does not emit X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, or any other X-RateLimit-* header, and no header reports your remaining spend. The only back-pressure signal on the response is Retry-After, returned alongside a 429 or 503. Drive your retry logic off the status code and Retry-After, not off a rate-limit counter.

To know where a key stands before you send a request, ask for it: GET /api/v1/key returns that key's limit, limit_remaining and limit_reset, and GET /api/v1/credits returns the account balance. Those are ordinary requests on a separate rate-limit tier, not headers — poll them on a schedule, not per call. See Management API.

Upstream anthropic-ratelimit-* headers are not forwarded either. They describe the provider's account allowance, not yours, so passing them through would tell you nothing usable about your own request budget.

On streaming requests there is one carve-out: a terminal failure that happens after heartbeat comments have started arrives as an in-stream error frame over HTTP 200, with no Retry-After header — drive stream retries off the in-stream error frame. See Streaming.

Rate limiting

Onlist enforces no request-rate cap. There is no requests-per-minute ceiling, no concurrency limit, and no variation by model or provider.

What each API key does carry is a spend budget: an optional total credit cap plus 5h, 1d, and 7d windows. These meter money, not calls, and every one of them defaults to unlimited, so you set them only if you want to bound what a single key can spend. Exhausting a window returns 429 with the rate_limited error code and a Retry-After header; exhausting the total cap returns 402 with quota_exhausted.

HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{
  "error": {
    "message": "rate limit 1d exhausted (10000/10000), retry after 42s",
    "type": "onlist_error",
    "code": "rate_limited",
    "param": ""
  }
}

These are fixed windows, not sliding ones. Each window is aligned to a wall-clock boundary derived from its own length, so a 5h window always starts at the same points in the day regardless of when you created the key. When a window ends, the spend counter resets to zero all at once rather than decaying gradually. Retry-After is the number of seconds until that reset, so honoring it is exact rather than an estimate.

Budgets are readable and settable programmatically. GET /api/v1/key reports the calling key's limit, limit_remaining and limit_reset; a management key can set them on any key with POST /api/v1/keys or PATCH /api/v1/keys/{hash}. The OpenRouter key model carries one limit per key, so that field covers the total cap and the 1d and 7d windows — the 5h window is Onlist-specific and is managed on the dashboard API Keys page. See Management API.

Note

The reporting endpoints under /api/ (usage logs, status) allow 180 requests per 180 seconds per IP. The OpenRouter-compatible account endpoints under /api/v1/ (key, credits, keys, generation, activity) are capped on their own tier: 60 requests per minute per credential and 500 per day per account, answered with the {"error":{...}} envelope those endpoints use. Inference traffic on /v1/ is affected by neither. Poll on a schedule rather than once per request and you will not meet these limits.

Cooldown

Separately from rate limiting, a selected provider or listing can be in cooldown, for example after a run of upstream failures. When the eligible pool for your request is unavailable for this reason, Onlist returns 503 with a Retry-After header.

HTTP/1.1 503 Service Unavailable
Retry-After: 60
Content-Type: application/json

{
  "error": {
    "message": "all matching listings have disabled upstream channels",
    "type": "onlist_error",
    "code": "no_provider_available",
    "param": ""
  }
}

Not every 503 carries a Retry-After: it is emitted specifically when the cause is a cooldown. Other 503 conditions, no_provider_in_max_price (your max_price filtered the whole pool) and no_provider_after_allow_deny (your allow/ignore preferences excluded every provider), reflect your request constraints rather than transient unavailability, so retrying the same request without changing it will not help. See Errors for the full status-code and error-code reference.

Tip

Treat 429 as "slow down" and a cooldown 503 as "this route is temporarily down." For both, wait the number of seconds in Retry-After before retrying. For a 503 without Retry-After, change the request (relax max_price, widen allow/ignore) rather than retrying blindly.

Reading the warnings header

X-Onlist-Warnings is a flat string. To turn it into a list you can branch on, split on the literal , separator. The header is absent when there is nothing to report, so guard for the missing case.

# Print the warnings header (if any) from a single request.
curl -sD - -o /dev/null https://onlist.io/v1/chat/completions \
  -H "Authorization: Bearer YOUR_ONLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-4o","messages":[{"role":"user","content":"hi"}]}' \
  | grep -i '^x-onlist-warnings:'

In production, log X-Onlist-Route-Id alongside each request so you can trace how it was routed, and surface X-Onlist-Warnings during development so you notice when a provider field you set is being ignored.

Next steps

  • Provider routing: Which provider fields are honored, and which are accepted-but-ignored and reported in X-Onlist-Warnings.
  • Errors: The error envelope, every status code, and the stable error code slugs to branch on.
  • Usage & cost: Token usage fields, and how to read per-request cost and provider from the usage log.
  • Management API: Read a key's remaining spend, look up what one call cost, and manage keys programmatically.
  • OpenAPI spec: Full request and response schemas, including every response header (YAML download).