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.
{
"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 request body. | Send valid JSON with a model and messages. |
401 | Missing, unknown, or revoked API key. | Check the Authorization header, or mint a new key. |
402 | Balance exhausted. | Add credit. |
403 | Account suspended. | Contact us. |
404 | Unknown model, or unknown previous_response_id. | Call GET /v1/models, or check the id. |
429 | Rate limited. | Back off for Retry-After seconds. |
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"
}
}Rate limits
Every response carries:
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 — generous, because spend is already gated by your balance; this 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, 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.