Docs / Errors
Errors and rate limits
Errors use the OpenAI error object, so the SDKs raise their usual exceptions. The status codes and codes below are the complete list the relay produces.
The error object
{
"error": {
"message": "Your credit balance is exhausted. Top up to continue.",
"type": "insufficient_quota",
"code": "insufficient_quota",
"param": null
}
}paramis alwaysnull.codeisnullwhere the table says so.- Errors from the model provider are never forwarded. When a model rejects a request, you get the status it returned with a generic message. Check the parameters you sent against this documentation.
- Failed requests that produced no output are free.
Status codes
| Status | code | type | When | Retry? |
|---|---|---|---|---|
| 400 | null | invalid_request_error | The body is not a JSON object. | No |
| 400 | missing_model | invalid_request_error | No model in the body. | No |
| 400 / 413 / 422 | null | invalid_request_error | The model rejected the request parameters. The status is the model's; the message is generic. | No — fix the request |
| 401 | missing_api_key | invalid_request_error | No bearer token. | No |
| 401 | invalid_api_key | invalid_request_error | Unknown or revoked key. | No |
| 401 | api_key_expired | invalid_request_error | The key passed its expiry date. | No — create a new key |
| 402 | insufficient_quota | insufficient_quota | Balance is zero or below, or held by requests in flight. | After a top-up, or once parallel requests settle |
| 402 | key_spend_limit_reached | insufficient_quota | This key used up its own spend limit. The account may still have balance. | Use another key |
| 402 | bonus_not_applicable | insufficient_quota | Only bonus credit is left and the model accepts paid credit only (model-one). | Use another model or top up |
| 403 | account_suspended | permission_error | The account is suspended. | No — contact us |
| 404 | model_not_found | invalid_request_error | Unknown or disabled model id. | No |
| 404 | not_found | invalid_request_error | Unknown path. | No |
| 405 | method_not_allowed | invalid_request_error | Completion endpoints take POST only. | No |
| 429 | rate_limit_exceeded | rate_limit_error | This key passed its request rate limit. | Yes, with backoff |
| 429 | upstream_rate_limited | rate_limit_error | The model is temporarily overloaded. | Yes, with backoff |
| 502 | null | upstream_error | The model returned an error or an invalid response, or could not be reached. | Yes |
| 503 | null | upstream_error | No route to the model is available right now. | Yes |
| 503 | billing_unavailable | api_error | Billing could not reserve credit for the request. | Yes |
| 504 | null | upstream_error | The model did not finish in time. | Yes, with a smaller max_tokens or a lower reasoning effort |
Checks run in this order: key (401), path and method (404, 405), account (403), empty balance (402), rate limit (429), request body (400), model (404), credit reservation (402), then the model itself.
An empty balance is 402, not OpenAI's 429, so SDKs do not retry it in a loop. The OpenAI SDKs do retry 429 and 5xx on their own by default.
A stream that fails after it started cannot change its status. It ends with an in-band error event instead; see Errors inside a stream.
import openai
try:
response = client.chat.completions.create(model="model-one", max_tokens=1024, messages=messages)
except openai.APIStatusError as e:
if e.status_code == 402 and e.code == "bonus_not_applicable":
... # only bonus credit left: switch to model-zero or model-one-flash, or top up
elif e.status_code == 402:
... # balance exhausted: top up; retrying will not help
else:
raiseRate limits
- 120 requests per minute per API key. Accounts with Scale perks get 600.
- The counter is kept per Cloudflare location, so traffic from several regions can briefly exceed it. Do not rely on that.
- A completion request counts once it passes the key, account and balance checks, even if it fails later.
GET /v1/modelsdoes not count. - There is no token-per-minute limit and no rate-limit headers. Over the limit you get
429 rate_limit_exceeded; back off and retry.
Request ids
Successful completion responses carry an X-Request-Id header (req_…). Error responses do not. Every request that got as far as the model — including ones that failed there — is listed in the dashboard with its status, tokens and cost. That is the place to look when a charge surprises you.