Core API
Errors & rate limits
Parse gateway and upstream errors, decide when to retry, and collect a trace identifier.
On this page
This page is for developers diagnosing failed production requests or designing retry behavior.
Most gateway-generated 401, 402, and 429 responses use a string in error. Upstream errors commonly use an OpenAI-compatible object, and so do two gateway errors: a revoked key (401) and a blocked tool (403). Clients must handle both. Read error.code when error is an object; a spending-limit 402 carries a top-level code instead.
{"error":"Unauthorized: Missing API key"}{"error":{"message":"The selected model rejected the request.","type":"invalid_request_error"}}Status codes#
| Status | Meaning | Retry? | Action |
|---|---|---|---|
| 400 | Invalid request, including unsupported masked image edits. | no | Fix the body or form fields. |
| 401 | Missing or unknown API key, or a rotated or revoked key (key_revoked). | no | Fix server-side authentication or switch to the new key. |
| 402 | Expired, disabled, parent-disabled, or depleted key, or a spending limit reached (key_daily_limit, key_monthly_limit). | no | Inspect key state, balance, and limits. |
| 403 | A tool blocked for the account (tool_not_allowed), or an upstream authorization or policy rejection. | usually no | Remove or allow the tool, or inspect the upstream body. |
| 404 | Unrecognized path prefix, a model that cannot serve the requested endpoint, or a missing account record (not_found). | no | Correct the endpoint or the id. |
| 408 | Upstream timeout response. | before output only | Retry with bounded backoff if the operation is safe. |
| 413 | Upload too large, including image edits over the combined 8 MB limit. | no | Shrink or split the upload. |
| 429 | CLSSAI free daily pool exhaustion, an account-endpoint budget (rate_limited), or an upstream limit. | later | Read the body and Retry-After, then wait before retrying. |
| 502 | All gateway connection attempts failed. | yes | Retry with bounded backoff. |
| 503 | Gateway configuration or upstream availability failure. | yes | Retry later and report the trace if persistent. |
| 504 | Upstream gateway timeout. | yes | Retry before output with bounded backoff. |
CLSSAI does not impose a per-key RPM limit on inference traffic. Free users share a global daily pool; exhaustion returns the gateway 429 string below. Account metadata routes have their own small budgets (see Usage & billing). Other 429 responses come from the upstream API and may use a different body or headers.
{"error":"Too Many Requests: Daily Free Quota Exhausted","message":"The global free tier limit for today has been reached. Please add funds."}Not found and account rate limits#
Newer gateway-generated errors use the OpenAI-compatible object shape with type, param, and code.
{"error":{"message":"This API endpoint is not supported.","type":"invalid_request_error","param":null,"code":"not_found"}}not_found (404): the path is not a supported gateway endpoint, the model record does not exist, or a generation id does not resolve to a request owned by the calling key. Unsupported platform paths (for example file or batch management) intentionally return this code instead of forwarding. A generation lookup also returns not_found when the log row is not yet written (about one minute after the request) or when the id belongs to another account. Do not retry not_found unchanged.
{"error":{"message":"Too many account requests. Retry after 1 seconds.","type":"invalid_request_error","param":null,"code":"rate_limited"}}rate_limited (429): the calling key exceeded a metadata-route budget — GET /v1/key and GET /v1/credits share 30 requests per 10 seconds, and GET /v1/generation allows 5 per 10 seconds with 1 lookup in flight. Read the integer Retry-After response header (seconds until the next slot) and wait. This code is distinct from the free daily pool string above and from upstream 429 responses.
Key and account errors#
These gateway errors come from the key or from account settings. Retrying the same request does not help.
{"error":{"code":"key_revoked","message":"This API key was rotated or revoked. Use the new key from your CLSSAI console."}}key_revoked (401): the key was rotated and its grace period has ended, or it was revoked. Switch to the replacement key.
{"error":{"type":"permission_error","code":"tool_not_allowed","tool":"web_search","message":"The web_search tool is turned off for this account. Remove it from the request or enable it in your CLSSAI console."}}tool_not_allowed (403): the account blocks this tool in Workspace tools. tool is one of web_search, web_fetch, code_execution, image_generation, or file_search. Remove the tool from the request, or allow it there.
{"error":"Payment Required: Key spending limit reached","code":"key_daily_limit","message":"This API key reached its daily spending limit of $10.00. The limit resets at 2026-09-23 00:00 UTC."}key_daily_limit and key_monthly_limit (402): a spending limit set in Workspace keys was reached. A limit on a console key counts that key's spend. A limit on the default Account Key counts the whole account's spend and stops requests from every key. Daily limits reset at 00:00 UTC and monthly limits on the 1st of the month (UTC); message names the limit and the reset time. Limits are soft: requests already in flight can take spend slightly past the limit. Limits are soft: the limit counter can run up to about a minute behind and can undercount under heavy concurrent traffic, so spend can go past a limit before requests stop. Billing itself is always exact; set limits below the amount you must not exceed.
Parse both shapes and retry safely#
const retryable = new Set([408, 429, 502, 503, 504]);
async function requestWithBackoff(url, init, attempts = 3) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
const response = await fetch(url, init);
if (response.ok) return response;
const body = await response.json().catch(() => ({}));
const message = typeof body.error === "string"
? body.error
: body.error?.message ?? body.message ?? `HTTP ${response.status}`;
if (!retryable.has(response.status) || attempt === attempts - 1) throw new Error(message);
await new Promise((resolve) => setTimeout(resolve, 500 * (2 ** attempt)));
}
throw new Error("Request failed");
}Do not retry after an SSE stream has emitted its first event. The gateway itself makes at most two attempts for connection-level 502, 503, and 504 failures, except for request bodies too large to replay.
Trace a request#
Inference responses do not add an application-specific request ID. Record the cf-ray response header on every response, plus x-generation-id when an OpenAI-compatible response includes it. Share those identifiers, the timestamp, endpoint, and status with support; never share the API key.
Common mistakes#
- Do not assume
erroris always an object, and do not retrykey_revoked,tool_not_allowed, or spending-limit errors unchanged. - Do not infer that a path exists from a 401; authentication happens before route selection.
- Do not invent a per-key RPM threshold.
- Include
/v1in the OpenAI SDK base URL and omit it from the Anthropic SDK base URL. - Keep server calls out of the browser, preserve complete variant IDs, and check empty streaming
choicesarrays. - Record
cf-rayand, for OpenAI-compatible routes,x-generation-idwhen present.
Find answers to common questions or diagnose a failed request.
Frequently asked questions →Troubleshoot errors →