Errors and retries
A failed order is charged nothing, with one exception the 500 row names: a response that was produced and delivered but could not be completed. The codes below distinguish a problem you can fix from one you should retry, and each row says what was charged.
The error envelope
Chat, completions and Responses HTTP failures use the OpenAI error shape. Branch on code where it is set, and on type otherwise: the table below names which field carries the identifier for each status. Where a Retry-After header is sent it is in whole seconds. Every response carries an x-request-id header, which the OpenAI SDKs expose as _request_id; quote it when you write to us about a failure and we can find that exact request in the gateway log.
{ "error": { "message": "no price limit set for model '<model_id>': every request needs one, and it is set per model. Pass price_limit (extra_body with the OpenAI SDK) or an X-Price-Limit header, or set a default for this model on the Bidding page (per key, or per account).", "type": "invalid_request_error", "param": null, "code": "missing_price_limit" }}Status codes
| Status | Identifier | Meaning | Charged | Retry |
|---|---|---|---|---|
| 400 |
| Malformed request, no price set anywhere, a retired model, or a request the model itself refused. model_preview: a request to a preview of the market, which opens soon; its prices are simulated until then, and the fixed-price models run today. fixed_price_model: a market endpoint asked about a fixed-price model, or a price limit set for one on the account or a key. unknown_model: a model filter naming no served model. image_url_unfetchable: an image URL that could not be fetched, with the part it sits in and the reason in the message | No | No, fix the request |
| 401 |
| Missing, invalid or revoked API key | No | No |
| 402 |
| Your available balance, after holds for orders already executing, cannot cover this request at the current clearing level, capped by the price you set, or at a fixed-price model's prices. A level of 0 needs no balance | No | After topping up, or when the level falls on a market model |
| 403 |
| Your account is not active: unverified, suspended, or deletion requested | No | No |
| 404 |
| The model is not available (model_not_found); no such order, file or batch (code null); or no such route (not_found). GET /v1/models lists the models served | No | No, fix the request |
| 408 |
| An unlocked order on a market model waited out its timeout (max_wait, else your account's order timeout) without starting | No | Yes, with a higher price or a longer max_wait |
| 409 |
| The order is executing or already finished, so it cannot be cancelled; the original of an idempotent replay was cancelled; a result cannot be deleted yet because its write or its batch has not finished; or a file cannot be deleted while a batch is still working from it | No | No, except a result whose write has not landed, which carries Retry-After |
| 410 |
| The result is gone: nothing was stored (storage is off by default), its storage period has passed, or you deleted it | As the original was | No. Use a new Idempotency-Key to execute again |
| 413 |
| Request body over the size limit (8 MB) | No | No, split the request |
| 429 |
| A rate limit or queue cap, a missed lock window (10 seconds), a request to a fixed-price model that did not start before its timeout, or result_pending while an already completed result is being stored | No new charge; result_pending keeps the original charge | Yes, after Retry-After where one is sent. SDKs do this |
| 500 |
| An error on our side. Where the response was already produced and charged the message says so | Recorded or uncertain; inspect the message and usage | Yes, unless the response was produced |
| 502 |
| The backend failed after part of a stream had been delivered. This one arrives inside the stream alone; no HTTP response carries it | No | Yes |
| 503 |
| Backend unavailable, a deploy in progress, unavailable object storage, or a stored result that could not be read. image_fetch_unavailable: image URLs cannot be fetched right now, and the same image sent as a base64 data URL is accepted | No new charge; a failed read keeps the original charge | Yes, after Retry-After where one is sent |
Two 429s mean different things. rate_limit_error is a ceiling on rate limits and clears with time. capacity_unavailable is a price-locked request that did not start inside its execute window, or a request to a fixed-price model that did not start before its timeout. A locked retry gets a fresh window at the standing quote, whose interval is published on the quote endpoint, and is never charged more than that quote; a fixed-price retry waits again at the model's prices.
The envelope on /v1/messages
POST /v1/messages answers every error in the Anthropic shape instead, {type: error, error: {type, message, code}}, with the same message and the same code as the rows above and the type from Anthropic's vocabulary. The Messages API page maps the two.
Retrying
| Do | Statuses | How |
|---|---|---|
| Retry with backoff | 429, 500, 503, and a 502 inside a stream | Honour Retry-After where it is sent. The OpenAI SDKs retry 408, 409, 429 and every 5xx on their own with exponential backoff, honouring Retry-After; an in-band error is yours to retry. |
| Retry after a change | 402, 408 | Add credits, or wait for the level to fall; raise the price limit or the timeout. |
| Do not retry | 400, 401, 403, 404, 409, 410, 413 | The same request gets the same answer. Fix it, or use a new Idempotency-Key where the row says so. |
A retry of an unlocked request re-queues it with a fresh deadline, so an SDK left on its default retries can wait several timeouts in a row. Set max_retries when you want a hard cutoff, and carry an Idempotency-Key so a retry can never run a successful request twice while that key remains valid.
Errors on a streaming request
A streaming request is answered 200 once its order is queued, and every failure after that arrives inside the stream in the wire's error event, with no status code and no Retry-After, so SDK retries do not engage. The streaming page has the frames.
Idempotency
Send an Idempotency-Key header of up to 255 characters on a chat or completion, Responses or Messages request to make retries safe. The key is scoped to your account, and a replay returns the original order's outcome rather than running it again. The work runs once and is charged once.
| The original order is | A replay gets |
|---|---|
| Still queued or executing | The same response, when it arrives. Chat and completions replay one chunk; Responses and Messages replay their event sequences. The replay waits without opening a stream, so its caller timeout still applies. |
| Done, with the result stored | The stored response, with the same usage and x_market. |
| Done, with nothing stored | 410 result_unavailable. Data storage is off by default, so this is the default after completion; use a new key to execute again. |
| Done, but its result is still being written | 429 result_pending with Retry-After. Retry with the same key. |
| Expired, failed or cancelled | The key is released, so the same key can run a new order. A replay arriving before release sees the original outcome; a persisted error can replay as 503. |
| Sent with different execution fields or price and wait headers | 409 idempotency_error. A key names one translated request. Native wires can replay after a change to an ignored or echo-only field. |
| Still being created | 429 with Retry-After of a second. The first request is between claim and insert. |
Caution
Once the order has finished, a replay can only return the original response if the result was stored. Data storage is off by default, so a replay after completion returns 410 result_unavailable. Use a new key to execute again, or turn storage on in Settings.
A key lives for the longest of one day, your storage period, and the order's own lifetime plus an hour, so a replay always works for the full life of a queued order. The fingerprint covers the endpoint, the body, the X-Price-Limit and X-Expires-After headers, and the key's own price limit for the model.