Errors and limits

View as Markdown
On this page

Errors come in two kinds. Ours, when we stopped the request: the key, the balance, limits, the model. Provider errors come through as is, in their protocol’s format: Claude Code and Codex recognise them and retry by themselves when it makes sense.

If you want a walk-through by symptom rather than a reference (“I got a 401, now what”), see Troubleshooting.

Format#

An error of ours is JSON in a shape both Anthropic and OpenAI clients parse. Look at error.code: different causes can share an HTTP status.

429
{
  "type": "error",
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "key rate limit exceeded"
  }
}

The x-should-retry header says whether a retry makes sense: true or false. When there is a time to wait, retry-after gives it in seconds. When the header and the Retry column of the table below disagree, trust the header: one code can have several causes. The 401 invalid_api_key and 503 authentication_unavailable answers come without this header.

Two refusals come not in this shape but as short plain text, with no JSON and no error.code: the 429 of the per-address limit (with a retry-after header) and the 413 for a request whose body size declared in Content-Length is over 32 MB. Both are covered in service limits.

Codes#

HTTPCodeWhat happenedWhat to doRetry
400invalid_request_bodyThe request body is not a readable JSON object.Check the JSON and the Content-Type header.no
400model_requiredThe request has no model field, or it is empty.Name a model.no
400duplicate_modelThe model field appears at the top level of the body more than once.Keep one model field.no
400model_not_availableThe model is not on our list, or not on this protocol.Take an id from GET /v1/models for your protocol.no
400request_too_largeOne request is estimated above the key’s whole per-minute token limit.Shorten the request or increase the key’s token limit in the dashboard; waiting won’t help.no
400invalid_surfaceGET /v1/models got an unknown surface value.Use chat, responses or anthropic.no
401invalid_api_keyThe key is missing, copied in part or revoked.Check the key header or create a new key in the dashboard.no
402insufficient_balanceThe balance can’t cover the reserve for the request.Top up or lower max_tokens; retrying before that won’t help.no
403model_not_allowedThe key is restricted to some models, and this one is not among them.Use an allowed model or add this one to the key’s model list in the dashboard.no
408request_timeoutThe request body took too long to read.Retry the request.yes
413request_too_largeThe request body is over 32 MB.Trim the history or the attachments.no
429rate_limit_exceededThe key’s per-minute limit on requests or tokens ran out.Wait as many seconds as retry-after says.yes
429concurrent_requestsThe key already runs the most requests it may at once: 4.Retry in a second, or spread the load across keys.yes
429monthly_limit_exceededThe key’s monthly spend cap ran out.Wait for the next month or increase the key’s monthly cap in the dashboard.no
502upstream_unavailableThe model’s provider did not answer.Retry later unless x-should-retry says false. Nothing was charged.yes
502upstream_auth_failedThe provider rejected our access. The failure is on our side, not your key’s.Contact support. Nothing was charged.no
503at_capacityThe service is overloaded: too many requests at once.Retry in a few seconds, following retry-after.yes
503authentication_unavailableWe could not check the key. The key itself is fine.Retry the request; no new key is needed.yes
503billing_unavailableWe could not reserve money for the request.Retry the request unless x-should-retry says false. Nothing was charged.yes
503pricing_unavailableThe model has no price set on this protocol.Use another model and contact support.no
503global_limit_exceededThe monthly budget of the whole service ran out.Contact support; your balance is untouched.no
504upstream_timeoutThe provider did not finish within 30 minutes.Retry the request; ask for long answers as a stream.yes

The code request_too_large comes with two statuses: 413 when the body is over the size limit, 400 when the request is above the key’s per-minute token limit. The 413 arrives as JSON when the body size is not declared up front (chunked); an ordinary request with Content-Length gets the 413 as plain text.

524 comes not from our API but from Cloudflare in front of it: a request without streaming waited more than about two minutes. Ask for long answers as a stream; see the API reference.

Codes in the request log#

A request we refused before sending it to the provider (401, 402, 403, our 400 and 429) does not reach the dashboard’s log and costs nothing. Requests that reached the provider are all in the log, with the HTTP status and an error code. Besides the codes of the table above, the log has:

CodeWhat happened
upstream_errorThe provider answered with an error. The client got it as is; the request is not charged.
upstream_error_bodyThe provider answered with status 200 but an error in the body. The client got it with status 400 or 502; the request is not charged.
upstream_stream_errorThe stream started with status 200, but the provider sent an error in it before any text, or ended it with nothing at all. The response status can no longer change, so the code shows only here; the request is not charged.
upstream_read_errorThe provider’s non-streamed answer broke off while we read it. The client got 502; the request is not charged.
settle_failedThe answer was delivered but the charge failed, on our side. The reserve returned; the request is not charged.
client_closed_requestThe client closed the connection before the answer ended. The row counts as canceled; what the model had generated is charged.

The stats count as an error only a failure on our side: a 5xx answer or a broken stream. Rows with a 4xx answer show in the log with their status but stay out of the error count.

Key limits#

A key can have:

  • requests per minute (rpm); past the limit the key answers 429 rate_limit_exceeded;
  • tokens per minute (tpm); these are input tokens by estimate: the request body size in bytes divided by 4. Past the limit it is the same 429; a request that alone exceeds the whole limit is 400 request_too_large;
  • a monthly spend cap in dollars; when the reserve for a request no longer fits in it, the key answers 429 monthly_limit_exceeded until the next month;
  • a list of allowed models; the rest get 403 model_not_allowed and are hidden from the key in GET /v1/models.

Limits are set when the key is created on the Keys page and can be changed there at any time: the key itself stays the same, and the new values apply from its next request. A request already running finishes under the old limits. A separate key per tool or project is a simple way to see who spends what. More in Keys and limits.

Service limits#

These apply to every key, whatever its own limits:

  • at most 120 requests and 2,000,000 tokens per minute per key; the lower of this ceiling and the key’s own limit applies;
  • from one IP address, at most 60 requests per minute, 300 per five minutes and 1,200 per hour to each API endpoint (/v1/messages, /v1/chat/completions and the others are counted apart), however many keys are in use. For one machine this limit is stricter than the key’s. Past it comes a 429 as plain text, without JSON, with a retry-after header;
  • at most 4 requests at a time per key; the next one gets 429 concurrent_requests;
  • a request body of up to 32 MB, or else 413: as plain text when the size is declared in Content-Length, and with the code request_too_large when it is not;
  • one request lasts 30 minutes at most;
  • at most 20 active keys per account; creating one more returns 409, and revoked keys don’t count.