Errors & rate limits
Every error status the gateway returns, what each one means, and the rate limits that apply to your key.
Every error is a standard HTTP status with a JSON body, so your existing retry and backoff logic works unchanged.
Error format#
Errors come back as an error object inside detail. It has a stable code and a human-readable message; type repeats the code. Branch on detail.error.code in code, and use message for logs and for people.
{
"detail": {
"error": {
"message": "Invalid API key",
"type": "invalid_api_key",
"code": "invalid_api_key"
}
}
}Some errors add fields to the same object. A spend-limit error, for example, also says which limit was hit.
Status codes#
| Status | Meaning | What to do |
|---|---|---|
400 | Bad request: malformed body, an unsupported parameter, or an invalid provider value. | Check the request against the upstream API schema. Unknown parameters are rejected, not silently forwarded. |
401 | Invalid or missing API key. This also covers a disabled or expired key, and a key whose owner has left the organization. | Check the auth header. In Console → API keys, look for a Disabled, Expired, or Owner removed status. Rotate the key if it leaked. |
402 | A spend limit was reached (period_quota_exceeded), or the wallet is out of funds (insufficient_quota). | Raise the budget in Console → Budgets, or add credits in Console → Credits. A spend-limit error includes the period, limit, used, resets_at, and target_type. |
429 | Rate limit reached (rate_limit_exceeded). | Back off and retry. See below. |
502 | Upstream network error reaching the model provider. | Transient. Retry with backoff. |
503 | No provider could serve the request. Either all routing retries were used up, or no upstream meets your data policy (no_provider_matches_data_policy). | Retry, choose another model, or relax the data policy. |
504 | The upstream model timed out. | Lower max_tokens or pick a faster model. |
Why a 401 says so little#
The response is the same for a disabled key, an expired key, a removed owner, and a key that never existed. This is deliberate: if it differed, anyone holding an old key could probe whether a person is still in an organization. To find out why a key fails, check the key list in the console.
Spend-limit errors#
A 402 with period_quota_exceeded means a daily, weekly, or monthly budget was hit. Budgets reset on a fixed UTC schedule.
{
"detail": {
"error": {
"message": "Daily spend limit reached",
"type": "period_quota_exceeded",
"code": "period_quota_exceeded",
"period": "daily",
"limit": 50000000,
"used": 50000000,
"resets_at": "2026-10-14T00:00:00.000Z",
"target_type": "workspace"
}
}
}| Field | Meaning |
|---|---|
period | daily, weekly, or monthly. |
limit, used | The limit and the spend so far, in quota units: 500,000 units = $1. The example above is a $100 daily limit. |
resets_at | When the limit starts over, in UTC. |
target_type | Whose budget was hit: workspace or member. Use it to tell the caller who can raise the limit. Handle the case where it is absent. |
An out-of-funds wallet is a 402 with insufficient_quota. Add credits to clear it. See Credits.
Rate limits#
Each account can send up to 600 requests per minute, counted across all of its API keys. Image generation is limited to 60 per minute. Requests over the limit get a 429 with rate_limit_exceeded. Wait and retry.