Errors & retries
Every non-2xx response uses one envelope:
{
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits. Add credits or enable auto-refill.",
"status": 402,
"param": null,
"docs_url": "https://primateintelligence.ai/docs/errors#insufficient_credits",
"request_id": "req_01H…"
}
}
code— stable, machine-readable, from a closed registry. Codes are append-only; meanings never change.docs_url— deep link to the error registry page anchor for that code.request_id— include it in support requests; it indexes our logs.error.errors[]— onvalidation_failed, every violation is listed (param + message), not just the first.details— structured extras (e.g.details.meteronquota_exceeded).
The registry is machine-readable
curl -s https://api.primateintelligence.ai/v1/errors
Each entry carries two retry flags:
retryable— safe to retry the same request unchanged (with backoff)idem— idempotent to retry with the sameIdempotency-Key
The SDKs generate their retry policy from this table — if you build your own client, do the same and your retry logic can never drift from the API.
What to retry
| Situation | Codes | Strategy |
|---|---|---|
| Rate limited | rate_limit_exceeded, concurrency_limit_exceeded |
Wait Retry-After seconds, retry |
| Platform transient | inference_unavailable, db_unavailable, internal_error, capacity_exhausted, idempotency_unavailable |
Exponential backoff + jitter, same Idempotency-Key |
| URL fetch flake | url_fetch_failed |
Retry the create; check the source URL is reachable |
| Prompt didn't compile | parse_failed (422) |
Rephrase the prompt or send a structured query |
| Everything else | validation_failed, invalid_api_key, insufficient_credits, … |
Do not retry unchanged — fix the cause |
Idempotency (never double-create)
Send Idempotency-Key (any string, 1–255 chars; UUIDv4 recommended) on POST /v1/videos, /v1/analyses, /v1/webhook_endpoints:
- Same key + same body within 24h → the stored response replays with
Idempotency-Replayed: true - Same key + different body →
409 idempotency_key_reused - Body comparison is canonical (JCS) — key order / whitespace / number formatting never false-positive
The SDKs auto-generate keys on create calls, which makes network-level retries safe by default.
Resource-level errors
Two codes never appear as HTTP errors — only inside a failed resource's error field:
inference_error— the model failed on this input; safe to create a new analysisstuck_timeout— the platform lost the job; not billed; resubmit
Check analysis.error / video.error when status is failed.
A worked retry loop (Python, no SDK)
import time, requests
def create_analysis(body, key, attempts=4):
idem = str(uuid.uuid4())
for attempt in range(attempts):
r = requests.post(
"https://api.primateintelligence.ai/v1/analyses",
json=body,
headers={"Authorization": f"Bearer {key}", "Idempotency-Key": idem},
)
if r.ok:
return r.json()
err = r.json()["error"]
registry = requests.get("https://api.primateintelligence.ai/v1/errors").json()
flags = next(e for e in registry["data"] if e["code"] == err["code"])
if not flags["retryable"] or attempt == attempts - 1:
raise RuntimeError(f"{err['code']}: {err['message']} ({err['request_id']})")
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
(Or just use the SDK — this is exactly what it does, with the registry vendored at build time.)
See also
- Error registry — every code, one anchor each (the
docs_urltargets) - Rate limits & quotas
- Billing & credits — handling
insufficient_creditsgracefully