Errors
Error shapes & codes
Every failure returns a uniform envelope. The machine-readable field is `error.code` — never parse the message.
The envelope
error.json
{
"error": {
"code": "rate_limited",
"message": "You've hit 100 req/s for key sk_live_****. Retry in 1.2s.",
"request_id": "req_01HT4Q3X…"
}
}The `request_id` matches the `X-Request-Id` header. Include it when contacting support.
Error codes
| HTTP | Code | Meaning | Action |
|---|---|---|---|
| 401 | unauthorized | API key missing, invalid, or revoked. | Fetch a fresh key from the developer portal and redeploy. |
| 403 | forbidden | Key is valid but doesn't have the required scope. | Re-mint with the correct scopes (e.g. `orders:write`). |
| 404 | not_found | The resource does not exist (or belongs to another tenant). | Check the ID; tenants are strictly isolated. |
| 422 | validation_error | Request body failed schema validation. `data.errors` lists field problems. | Fix the payload. This is never worth retrying unchanged. |
| 429 | rate_limited | Token bucket for this key is exhausted. Retry-After header present. | Back off for `Retry-After` seconds, then retry. SDKs surface `.retry_after`. |
| 409 | idempotency_conflict | Same Idempotency-Key reused with a different body. | Mint a new key for the new body, or repeat the original body. |
| 500 | internal_error | Unexpected server error. Correlated in logs via `request_id`. | Retry with exponential backoff up to 3 times. |
| 503 | service_unavailable | Upstream dependency down (usually transient). | Check the status page, then retry with backoff. |
SDK behaviour
The SDKs raise a typed exception for each class. Narrow via `instanceof` / class match.
handle.py
from via import VIA, RateLimitError, AuthenticationError, NotFoundError
try:
via.orders.get("ord_abc")
except RateLimitError as exc:
time.sleep(exc.retry_after or 1)
except AuthenticationError:
rotate_api_key()
except NotFoundError:
return None