Errors
Errors are always JSON, always carry a stable machine-readable code, and never leak internal detail.
Error shape
Any non-2xx response has an error object. The code is stable and safe to switch on; the message is for humans and may change.
{
"error": {
"code": "invalid_request",
"message": "Unknown game \"pokemon-tcg\".",
"details": {
"field": "game",
"allowed": ["pokemon", "magic-the-gathering", "one-piece"]
}
}
}Status codes
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter failed validation. The response names the offending field. |
| 401 | unauthenticated | Missing or malformed API key. |
| 402 | no_active_plan | The account has no plan attached. Choose one to open the API. |
| 402 | balance_empty | The account balance will not cover this call. Top up to resume. |
| 403 | forbidden | The key is valid but the plan does not include this resource. |
| 404 | not_found | No card, set or game matches the identifier. |
| 409 | conflict | The webhook or export already exists. |
| 422 | unprocessable_query | The GraphQL query exceeded depth or complexity limits. |
| 429 | rate_limited | Quota exhausted. Check the Retry-After header. |
| 500 | internal_error | Something broke on our side. It is already paging someone. |
Retrying safely
Retry 429 and 5xx. Never retry 400, 401, 403 or 404 — the result will not change. A 402 means the account cannot pay for the call — either no plan is attached (no_active_plan) or the balance is empty (balance_empty). Neither resolves on its own, so alert a human rather than backing off. Honour Retry-After when it is present.
async function withRetry(fn, attempts = 4) {
for (let i = 0; i < attempts; i++) {
const res = await fn();
if (res.status !== 429 && res.status < 500) return res;
// Honour Retry-After when present; otherwise back off exponentially.
const after = Number(res.headers.get("Retry-After"));
const waitMs = Number.isFinite(after) ? after * 1000 : 2 ** i * 250;
await new Promise((r) => setTimeout(r, waitMs));
}
throw new Error("Exhausted retries");
}GraphQL errors
GraphQL follows the spec: transport errors use HTTP status codes, and resolver errors return 200 with an errors array. Each entry carries the same code under extensions, so you can share error handling between both interfaces.