Eterial Docs
API reference

Errors

Every status code and error type the API returns, and what to do about each.

Errors come back in the OpenAI envelope:

{
  "error": {
    "message": "Insufficient balance. Please top up your account.",
    "type": "insufficient_funds_error"
  }
}

Status codes

StatustypeWhen you see it
400invalid_request_errorMalformed body, missing model, or a capability the model cannot serve.
401invalid_request_errorKey missing or invalid. Carries code: missing_api_key / invalid_api_key.
402insufficient_funds_errorYour balance cannot cover the request.
403tenant_unavailable_errorThe account is suspended or unavailable.
404invalid_request_errorUnknown model.
409invalid_request_errorIdempotency conflict — a concurrent request with the same key is in flight.
413invalid_request_errorRequest body over 8 MiB.
429rate_limit_errorRate limit exceeded. Back off and retry.
500internal_errorOur fault. Quote the request id when you tell us.
503upstream_errorTemporarily unavailable, including when nothing can serve the model. Retry.
504upstream_errorThe model did not respond within five minutes.

code

Alongside message and type, an error carries a stable machine-readable code. It is the narrowest thing to branch on, and the only one that separates errors sharing a status: unsupported_capability and upstream_rejected are both a 400, and you do not handle them the same way.

codeStatusWhat it means
missing_api_key401No Authorization header was sent.
invalid_api_key401The key is unknown, expired or revoked.
insufficient_funds402Your balance cannot cover the request.
tenant_unavailable403The account is suspended or unavailable.
unknown_model404No such model id.
unsupported_capability400No backend serves this model with everything the request asks for.
upstream_rejected400The backend refused the request itself — change it before resending.
idempotency_conflict409A request with the same Idempotency-Key is still in flight.
rate_limit_exceeded429Too many requests on this key. Back off.
upstream_unavailable503Nothing could serve it just now. Retry.
gateway_timeout504The model did not answer in time.
not_routable500The model is listed but has no usable backend. Tell us.
internal_error500Our fault. Quote the request id.

Three errors come before `code` does

A body that cannot be parsed at all is rejected before any of this runs, so a malformed JSON body, a missing model and an oversized body answer with message and type alone.

Never branch on the wording of message. It is written for whoever is reading a log, and it changes when that reads better.

Failover is automatic

A backend dropping out is not one of these. The request is retried against the equivalent model on OpenRouter before any error reaches you — see Failover & retries.

Which of these to retry

Retry itFix it
429 — with backoff400 — the request is wrong
500401 — the key is wrong
503402 — top up
504 — if the work is worth it403 — talk to us
404 — the model name is wrong
409 — you already sent this
413 — the request is too big

Retries should be exponential and jittered — retrying immediately, or having every worker retry on the same schedule, turns one rejected request into a sustained wall of them.

Mid-stream errors are not statuses

Once a streamed response has begun, the status is already 200. A failure after that arrives as an error event inside the stream. A client that does not look for it will read a truncated answer as a complete one — see Streaming.

Finding a request afterwards

Every request has an id, and it is the fastest thing to give us when something looks wrong. It also links a charge on your balance to the request that caused it, and carries the disposition explaining what happened. See Request dispositions and the usage log.

On this page