Telluvian

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

StatusMeaningWhat to do
400Malformed request body.Send valid JSON with a model and messages.
401Missing, unknown, or revoked API key.Check the Authorization header, or mint a new key.
402Balance exhausted.Add credit.
403Account suspended.Contact us.
404Unknown model, or unknown previous_response_id.Call GET /v1/models, or check the id.
429Rate 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 window
  • X-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.