Errors and rate limits
Status codes, error shapes, and throttling.
Errors are OpenAI-shaped: a JSON body with an error object carrying
message, type, and code. The one exception is 422, covered below.
{
"error": {
"message": "Insufficient credits. Top up your balance in the dashboard.",
"type": "invalid_request_error",
"code": "insufficient_credits"
}
}Status codes
| Status | Meaning | What to do |
|---|---|---|
400 | Malformed JSON, invalid Model Select controls, or analyze without probe opt-in. | Check the body; analyze requires include_scores: true. |
400 | upstream_bad_request: the model provider rejected the request, often a parameter it does not accept or a prompt longer than its context window. | Check max_tokens, temperature and the message format. |
400 | byok_credential_rejected: your own provider key was rejected and nothing else could serve the request. | Check the key under Provider keys. See Bring your own key. |
400 | credential_not_found / credential_not_usable: the request's byok_credential names no key, or a key that cannot serve this model or this API key. | Check the slug under Provider keys, or drop the field to use the default order. |
401 | Missing, unknown, or revoked API key. | Check the Authorization header, or mint a new key. |
402 | Balance exhausted, or (byok_credential_rejected) your provider account is out of credit. | Add credit, or top up with the provider. |
403 | Account suspended, or the wrong kind of key: a management key on a model endpoint, or an ordinary key on /v1/byok. | Contact us, or use the right key. |
404 | Unknown model, or unknown previous_response_id. | Call GET /v1/models, or check the id. |
413 | upstream_request_too_large: the request is too large for the model. | Shorten the prompt, or use a model with a larger context window. |
422 | Request body failed schema validation: a non-string content, or an -analyze call with no assistant message. | Check the shape against Compatibility. |
429 | Rate limited by us, or (upstream_rate_limited) the model is rate-limited by its provider. | Back off for Retry-After seconds, or use your own provider key. |
502 | upstream_unavailable: the model provider returned an error. With your own key, byok_credential_rejected. | Retry with backoff, or try another model. |
503 | Model Select is unavailable. | Retry with backoff, or name a model yourself. |
504 | upstream_timeout: the model provider did not respond in time. With your own key, byok_credential_rejected. | Retry with backoff. |
A provider failure is always answered in our words, with type: "upstream_error"
and one of the upstream_* codes above. The provider's own error text is never
passed on.
422 has a different body shape
A 422 comes from the model server's own validation and is relayed
unchanged, so it carries {"detail": ...} rather than the error object
above. It is a string for the analyze case, and a list of field errors for a
schema violation. Nothing is billed for a rejected request.
A 404 for an unrecognised model carries code: "model_not_found" and names
the offending field in param:
{
"error": {
"message": "The model 'gpt-9' does not exist or you do not have access to it. Call GET /v1/models for the available models, or see https://telluvian.ai/docs.",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}Model Select
A Model Select control that cannot be
honoured is rejected before anything is generated, and nothing is billed. The
message names the field. A direct /v1/modelSelect call returns its own error
envelope with a stable code; see Model Select errors. The common causes:
| Status | Cause |
|---|---|
400 | xPerf sent as a quoted number, or naming a model we do not know. |
400 | xPerfEffort sent alongside a numeric xPerf, or an effort the named model does not offer. |
400 | xLatency outside instant, chat and batch. |
400 | modelZoo matching no active Model Select candidate. |
400 | sessionId that is not a GUID. |
503 | Model Select is unavailable. Nothing is substituted. See when Gallery is unavailable. |
A quality bar that nothing can meet is not an error: the request succeeds
on the best model available. Read response.model to see what ran.
Rate limits
Chat Completions and Responses normally carry these headers when rate-limit checks complete successfully:
X-RateLimit-Limit, requests allowed in the current windowX-RateLimit-Remaining, how many are left
A 429 additionally carries Retry-After, in seconds.
Limits are per API key, 600 requests per 60-second window by default. That is generous, because spend is already gated by your balance. It is an abuse ceiling rather than a quota. A rejected request still counts against the window, so hammering the endpoint keeps it pinned rather than earning a fresh allowance. If you need a higher limit, get in touch.
The limiter fails open
Counting happens centrally, not per server instance. The gateway runs on several at once, and an in-process counter on each would both admit multiples of the intended limit and reset every time an instance scales. If that count itself is unreachable (a database blip), the request is allowed through rather than rejected: a limiter that hard-fails every request during a partial outage would turn it into a total one. Your balance is the backstop either way. A burst let through during a blip still has to be paid for.
Balances can dip slightly negative
The balance check happens before your request runs, but the cost is settled
after it finishes. A request admitted with a small positive balance can take
it just below zero, bounded by your max_tokens. The next request is then
refused with a 402.
Streaming failures
A stream can fail after it has already started, after your client has
received a 200 and some content chunks. There is no structured SSE error
event for this: the connection is simply closed without a trailing
data: [DONE].
A stream without [DONE] is a failure, not a slow finish
Treat any stream that ends without the literal data: [DONE] line as
failed, not as "still coming." Retry the request rather than waiting.
Billing settles from whatever token counts were observed before the failure (or an estimate when final usage is unavailable), so a request that fails partway through can still incur a (smaller) charge for the tokens the model had already generated, the same way a partial response you kept and used would be billed. This is the same settlement path either way; see Pricing.