Errors & retries

Branch on error.code, not the message.

Error body

The message is for people and can change. An optional error.request_id identifies the error for investigation. Catalog errors may omit it.

{
  "error": {
    "code": "operation_not_enabled",
    "message": "Inference is temporarily unavailable. No funds were reserved.",
    "request_id": "00000000-0000-4000-8000-000000000001"
  }
}

Current /v1 errors

These are uRouter codes. Other services’ codes, headers and rate limits are not part of this API.

Current /v1 errors
HTTPCodeWhat to do
401invalid_api_keyCheck the Bearer credential, expiry and revocation. Retrying unchanged will not help.
404model_not_foundRead the catalog and use an exact model ID.
404request_not_foundCheck the UUID and use the owning logical key.
503catalog_unavailableRetry this read with bounded backoff.
503operation_not_enabledExecution is unavailable for this account or temporarily paused. No funds were reserved.
503service_unavailableTemporary service failure; preserve the error reference.

Retry policy

Retry failed reads with bounded exponential backoff and jitter. Honor Retry-After when present. Do not retry 401/404 or disabled operations unchanged. A timeout alone does not establish whether a write was accepted.

const response = await fetch("https://api.urouter.net/v1/models");
const body = await response.json();

if (!response.ok || body.error) {
  throw new Error(body.error?.code ?? `http_${response.status}`);
}
console.log(body.data);

Limits and idempotency

Chat accepts up to 60 requests per minute per logical key. A 429 includes Retry-After. Send an optional Idempotency-Key of 1–128 letters, digits, hyphens or underscores for each intended chat request. Reusing an admitted identity returns 409 without redispatching. Check the accounting request before retrying; responses are not replayed. Reseller key writes have their own required UUID Idempotency-Key and reconciliation endpoint; do not assume a replay returns the original secret.