Docs
Sign inStart free
Documentation/Errors & status codes

Errors & status codes

Every status the STELQ gateway actually returns — 400, 401, 402, 403, 404, 405, 409, 502, 503 — all in one uniform {"error": string} shape, with which are safe to retry and which are fatal until you fix something. This is the cross-service error contract; each service's own Errors section is the same table, filtered to that route.

5 min readHumans & agentsopen as text

01The uniform error shape

Every error the gateway returns is the same JSON object — a single error string — served as application/json. There is no code field, no nested details, no per-service envelope. If a call fails, you parse one shape and read one human-readable message. Branch on the HTTP status; read error for the why.

the only error shape you ever get
{ "error": "Insufficient balance. This request costs $0.0010 but your balance is $0.0000. Please top up at https://stelq.com/dashboard/billing." }

This is the gateway-wide, cross-service error contract. It complements the Errors section on each service doc: a service's table is this same set, narrowed to the statuses that route can produce. Search, Content, and Answers share one synchronous proxy path; Research adds a couple of statuses of its own (409, and a 503 distinct from the auth one). The shape never changes between them.

Successful responses carry an X-Request-Id (and, on the sync services, X-Latency-Ms). Quote X-Request-Id when you report a problem — it ties your call to a row in the platform's request log.

02The complete table

Every status the gateway can originate, end to end — auth, balance, routing, and the upstream-down fallback. An upstream engine 5xx is passed through with its own status, but the values below are what the gateway itself produces.

StatusWhereMeaning
400Any serviceInvalid request — a field is missing or malformed. The error string names the field, e.g. "limit must be a positive integer." or "query is required."
401Any serviceMissing, malformed, invalid, revoked, or expired API key. Includes a missing or non-Bearer Authorization header.
402Any serviceInsufficient balance to cover the request, or the key has hit its credit limit. The message says which, and includes the dashboard top-up link.
403Any serviceThis API key is paused. Resume it in the console; the key itself is still valid.
404Any serviceUnknown route. The body lists the routes that do exist in an available array.
405Any serviceMethod not allowed — you sent a non-POST to a known POST route, or a GET to /v1/mcp. The generic case returns a plain "Method not allowed." body; only the GET /v1/mcp probe also sets an Allow: POST, OPTIONS header.
409Research onlyA research job for this exact request is already in flight (idempotency dedupe). Comes with Retry-After: 2.
502Any serviceUpstream engine unreachable — transient. Safe to retry. Not charged.
503Auth / ResearchThe authentication service is down, or (Research submit) the idempotency claim store is unreachable. Transient; the Research case sends Retry-After: 2.

03What is safe to retry, and Retry-After reality

Split the table into two halves so an agent can self-heal. Transient statuses mean the gateway or an upstream is momentarily unhealthy — retry with backoff and the same call will likely succeed. Fatal statuses mean the request itself is wrong — retrying the identical call changes nothing; something (the body, the key, the balance) has to change first.

StatusRetry?What to do
502YesUpstream is down. Retry with backoff. Not charged, so a retry costs nothing extra.
503YesAuth or the Research claim store is down. Retry with backoff; honor Retry-After on the Research case.
409Yes, brieflyResearch duplicate in flight. Wait Retry-After seconds, then poll the original job instead of resubmitting.
400NoFix the request body — the error string names the offending field.
401NoFix the credential. Rotate or replace the key.
402NoTop up credits, or raise the key's credit limit. Then retry.
403NoResume the paused key in the console, then retry.
404 / 405NoFix the route or method — check the available array on a 404; for a 405, POST to the route.

On Retry-After: the gateway sets it in exactly two places, both Research-only, both hardcoded to 2 seconds — the 409 (duplicate in flight) and the 503 (claim store unreachable) on submit. The synchronous services — Search, Content, Answers — never send it. If you see Retry-After, you are talking to Research submit.

No 4xx or 5xx is ever billed. The charge is computed as cost when status < 400, else 0 on both the sync proxy and Research submit — so a 502, a 402, a 400, every failure records zero cost. A retry after a transient failure does not double-charge you.

The two Research-specific statuses here — the 409 dedupe and the claim-store 503 — are part of the idempotency design on research submit, not generic gateway behavior. See the cross-links below for how credits and balance produce the 402, and how Research idempotency produces the 409/503.

04Related