Getting started
Errors
Error envelope, status codes, and retry guidance.
Error envelope
All error responses share the same shape:
{
"error": {
"code": "SNAKE_CASE_CODE",
"message": "Human-readable explanation."
}
}Unlike successful responses, error bodies are not wrapped in meta — they're just the
error object. Some errors include additional structured fields alongside code and
message — NOT_SUPPORTED carries league and capability fields to aid feature
detection. Most do not.
Status codes
| Status | Meaning | Retry? |
|---|---|---|
400 Bad Request | Invalid parameters (bad date format, unknown stat key, bad stage selector) | Fix the request |
401 Unauthorized | Missing or invalid API key | Check your key — see Auth |
403 Forbidden | Free-tier key calling a paid endpoint (TIER_REQUIRES_PAID) | Upgrade — don't retry |
404 Not Found | Resource doesn't exist (person, team, contest, competition) | Don't retry |
429 Too Many Requests | Rate limit (RATE_LIMIT_EXCEEDED) or monthly quota (QUOTA_EXCEEDED) — branch on error.code | See below |
500 Internal Server Error | Something broke on our side (INTERNAL_ERROR, CACHE_ERROR) | Retry once, then page support with request_id |
501 Not Implemented | Capability not supported for this competition (NOT_SUPPORTED) | Don't retry — check capabilities |
502 Bad Gateway | An upstream data provider failed (PROVIDER_ERROR) | Retry with exponential back-off |
503 Service Unavailable | All providers failed, DB pool unavailable, or auth backend down | Retry with exponential back-off |
For the full machine-readable code catalog (UNAUTHORIZED, QUOTA_EXCEEDED, RATE_LIMIT_EXCEEDED,
NOT_SUPPORTED, UNKNOWN_STAT, …), see Error codes.
Retry guidance
- 429 — read
error.codefirst.RATE_LIMIT_EXCEEDEDmeans the per-second cap: back off a second or two and continue.QUOTA_EXCEEDEDmeans the monthly unit cap: retrying won't help until the billing period resets or you upgrade (X-RateLimit-Resetcarries the period-end timestamp on quota responses). - 502 / 503 — use exponential back-off starting at 1 s, cap at 30 s, give up after 3–5 attempts.
- 500 — retry once; if it persists, page support with the
request_id.
Everything else (400, 401, 403, 404, 501) is deterministic; retrying without changing the request, key, or plan will produce the same result.