Rate limits & errors
Limits are enforced per API key at your plan's budget. When you hit one, the response tells you exactly how long to wait.
Budgets per plan
| Plan | Requests / minute | Requests / day |
|---|---|---|
| Developer | 10 | 500 |
| Plus | 60 | 5,000 |
| Pro | 180 | 20,000 |
The per-minute budget is enforced hard (429); the daily figure is the fair-use ceiling we expect you to stay under. Sustained abuse leads to key suspension, not surprise bills — there are none.
Handling 429
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 60
{ "error": { "code": "rate_limited",
"message": "Rate limit exceeded — the Developer plan allows 60 requests per minute." } }Sleep for Retry-After seconds, then continue. If you page through large datasets, pace your requests instead of bursting — the window is sliding, not fixed.
Caching
Catalog responses carry Cache-Control: public, s-maxage=300, stale-while-revalidate=600 — the data changes slowly, so cache on your side too and you will rarely touch your budget. /api/v1/random and /api/v1/me are never cached. For mirrors, poll /api/v1/changes instead of re-crawling.
The error envelope
Every non-2xx response has the same shape:
{ "error": { "code": "not_found", "message": "No catalog entry with that xtv_id." } }| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_* | A parameter failed validation — the message says which and why. |
| 401 | missing_api_key | No API key in the request. |
| 401 | invalid_api_key | Key unknown, revoked, or the developer account is suspended. |
| 404 | not_found | The id or slug resolved to nothing. |
| 429 | rate_limited | Over your plan's budget — respect Retry-After. |
| 500 | internal | Our fault. Retry with backoff; report if it persists. |
Branch on error.code, not the message — messages are for humans and may be reworded.