Skip to main content

Errors

Every error — from any endpoint, and from every MCP tool — has the same body:

{
"error": {
"code": "insufficient_credits",
"message": "You need 240 more credits to run this."
}
}

code is a stable machine-readable string. message is written for humans and will change; never parse it or branch on it.

Codes

CodeStatusCauseWhat to do
invalid_params400The body or query doesn't match the endpoint contract — a missing field, an unknown storageId, a duration the model doesn't support.Read message; it names the offending field and, for capability mismatches, lists the valid values. Fix and retry.
unauthenticated401Missing, malformed, unknown, or revoked API key.Check the Authorization: Bearer corte_sk_… header. If the key was deleted, issue a new one. Not retryable.
insufficient_credits402The caller's balance can't cover the estimate.Top up or upgrade. Nothing was charged.
owner_insufficient_credits402Same, but for project-billed work — the project owner's balance is short, not the caller's.The caller can't fix this; the owner must top up.
plan_required402The model is paidOnly and the paying account has never bought anything.Pick a model with paidOnly: false, or make a top-up or subscribe — either unlocks the full catalog.
subscription_required402The action needs an active subscription.Subscribe, or use a free-tier path.
storage_limit402The account is at its plan's storage quota. Blocks new uploads and imports only.Delete objects with DELETE /v1/storage/:storageId or upgrade. Generation results still save.
forbidden403Authenticated, but not allowed: a contributor doing an owner-only action, a workspace with the feature disabled, deleting a default project.Not retryable as-is. Check the caller's project role.
not_found404Unknown id — or an id owned by someone else. Ownership failures are deliberately indistinguishable from missing rows.Verify the id and that the key's account can reach it.
conflict409A client-supplied id already exists (project creation), or a versioned write is stale.Re-read current state and retry with fresh values.
payload_too_large413Request body over 32 MB, or an upload over the 1 GB per-file limit.Send media through Files rather than in a JSON body.
rate_limited429An upstream provider rate-limited the request. Corte does not impose per-key limits.Retry with exponential backoff.
provider_unavailable503An upstream model or LLM provider is down or returned something unusable.Retry with backoff; try another model if it persists.
internal500An unhandled server error.Retry once; if it repeats, contact support with the timestamp.

Errors that cost nothing

Credit checks run before any work is submitted to a provider. A 402 of any kind therefore means no credits were spent — the estimate simply didn't fit. The same is true of invalid_params from capability validation: parameters are checked against the model's declared capabilities before the job is created.

Failures that arrive later

A request can succeed and the work still fail, because generation is asynchronous. A generation that reaches status: "failed" carries the reason in errorMessage; a workflow run that reaches status: "error" carries per-node detail in nodeResults[nodeId].detail. Neither is an HTTP error — the poll returns 200 with a failed record.

Handling pattern

import time, requests

BASE = "https://api.corte.so"
HEAD = {"Authorization": f"Bearer {KEY}"}

def submit(body):
r = requests.post(f"{BASE}/v1/generations", json=body, headers=HEAD)
if r.status_code == 200:
return r.json()["job"]

code = r.json().get("error", {}).get("code")
if code in ("insufficient_credits", "owner_insufficient_credits", "plan_required"):
raise OutOfBudget(code) # nothing was charged; don't retry blindly
if code in ("rate_limited", "provider_unavailable", "internal"):
time.sleep(5) # transient upstream — back off and retry
return submit(body)
raise ValueError(r.json()["error"]["message"]) # your bug: fix the request

Three buckets cover it: fix the request (400/401/403/404/409/413), fix the budget (402), wait and retry (429/500/503).