API reference

Errors

Errors use the envelope of the protocol you call. Branch on its stable error fields; never parse the human-readable message.

HTTPTypeMeaning & remedy
400invalid_requestMalformed body, a value out of range, or no model to route to. Fix it before retrying.
400unsupported_parameterA parameter this model or endpoint cannot take, such as an unknown provider key. The message names it.
400content_policy_violationThe provider or capability refused the content. Change the input, not the timing.
401authenticationMissing, unknown or revoked key.
402spend_cap_exceededThe key is at its monthly spend cap. Nothing was sent upstream. See Rate limits & caps.
402insufficient_creditsYour organization's prepaid funds cannot cover the request, or the organization is not enabled for billing. Add funds on the console's Billing page, then retry; if funds are already there, contact support. See Cost.
403permission_deniedYour role or key scope cannot perform this action. Members cannot change keys or settings, and only owners change billing and members.
404not_foundUnknown model, a model that this endpoint does not serve, or a job or file outside your workspace.
409conflictAn Idempotency-Key reused with a different body, a job-state conflict, or metadata.reason: quote_changed on a guarded media estimate.
413payload_too_largeThe body or an uploaded file is over the endpoint's size limit.
429rate_limit_exceededThe key is over a rate limit you set (metadata.reason key_rpm or key_tpm), a provider throttled the request, or your workspace has too many requests or jobs in flight. Honour Retry-After when present; a concurrency refusal can omit it. See Rate limits & caps.
500internalThe gateway failed. Completion may be uncertain; use the endpoint-specific guidance below.
502provider_unavailable, upstream_error, protocol_error, capability_failedThe provider failed, timed out, or answered in a shape AI Hub could not use. Reconcile uncertain work before submitting it again.
503provider_unavailable, saturated, shutting_downNo provider could take the request, or the gateway is busy or restarting. Honour Retry-After when sent, then reconcile uncertain work before any new submission.

Additional controls return 402 workspace_budget_exceeded and 403 model_not_allowed. A key concurrency refusal uses 429 rate_limit_exceeded with metadata.reason: key_concurrency and no timed retry header. See Rate limits & caps.

Shape

json
{
  "error": {
    "code": 429,
    "type": "rate_limit_exceeded",
    "message": "…"
  }
}

For Chat Completions, code is the HTTP status repeated in the body, so an SDK that surfaces only the body can still branch. Some errors add metadata — a 402 carries spend_usd and cap_usd, and a saturated 503 carries retry_after_s. The set of type values is open: treat an unknown value as a generic failure of the HTTP status. Every error response carries the X-Generation-Id header; quote it when you contact support.

The Responses route uses string error codes; Messages uses an Anthropic error envelope. Use the reference for the protocol you call.

Protocol and job failures

SurfaceStable failure signalRecovery
Chat JSONHTTP status and error.type; numeric error.codeFix deterministic request/access refusals. A timeout or 5xx can follow dispatch; retain X-Generation-Id.
Chat SSEA frame with top-level error, or EOF without [DONE]HTTP may already be 200. Stop reading deltas, mark the answer incomplete and reconcile; do not replay automatically.
ResponsesJSON error.code is a string. SSE response.failed carries response.error.code.Require response.completed for success; response.incomplete reports a length stop. A failed event or interrupted stream is not a completed answer.
MessagesJSON type: error with error.type; SSE event: errorUse the Anthropic envelope, not Chat's numeric code. Do not read an error event as content or accept an interrupted stream as success.
Asynchronous jobsSubmit HTTP refusal versus an accepted job whose status becomes failedSave the job ID and read its returned error/reason. A 202 acknowledges acceptance, not completion. Polling the existing job does not submit another generation.

Upstream failures are normalized into the route's error shape. The set of types and reasons is open; for an unfamiliar value, keep the status, protocol, safe error fields and request or job IDs for support. Do not log keys, signed URLs or private input.

An accepted video with status: pending and queued: true is waiting to start. Poll its existing job ID instead of resubmitting. If its ten-minute wait expires before dispatch, status: failed with queue_expired: true and error.code: upstream_timeout reports an uncharged queue expiry; usage cost is zero and the reservation is released. This is distinct from an HTTP submission refusal or an expired download. See Video generation for cancellation and polling.

Reason metadata

metadata.reason refines a failure where present; it is not a universal retry flag. Key admission uses key_rpm, key_tpm and key_concurrency. no_provider_matches requires routing changes. idempotency_mismatch means the saved key/body pair differs; in_doubt means reconcile existing work. content_not_ready requires checking the job; content_expired means the output is no longer retrievable from that route. Character errors such as character_not_ready require checking the character before creating a video. See the operation schemas and guides for each family's reasons.

Media failure details

Supported media failures may add safe charged, alternatives and failed_provider fields in error.metadata. The charge value is true, false or unknown. Terminal video errors may carry the same safe metadata. Activity exposes only derived media_rerun details, never the original request body or arbitrary error metadata. See media offers and manually confirmed reruns.

Retrying safely and idempotency

After a timeout or 5xx, generation may have started. A new synchronous request can repeat work and cost. X-Generation-Id identifies a request; it does not deduplicate a retry.

  • Fix 400, 401, 402 and 403 causes before retrying.
  • A 503 whose metadata.reason is no_provider_matches needs different routing preferences. Repeating it unchanged cannot help.
  • For supported asynchronous submits, keep the same Idempotency-Key and body. Poll the existing job to reconcile an uncertain submission; do not invent a new key to retry it.
  • Retry read-only status lookups with bounded backoff for transient 429/5xx errors. Honour Retry-After when present. This does not authorize resubmitting generation.
python
import random, time
import requests

def get_job(url, api_key):
    # Reads an existing authenticated AI Hub job URL, never submits new work.
    for attempt in range(5):
        response = requests.get(url, headers={"Authorization": f"Bearer {api_key}"}, timeout=30)
        if response.status_code not in {429, 500, 502, 503} or attempt == 4:
            response.raise_for_status()
            return response.json()
        delay = response.headers.get("Retry-After", "")
        time.sleep(float(delay) if delay.isdigit() else min(2 ** attempt + random.random(), 30))