# Errors & limits

Errors are returned as a JSON object with an HTTP status; requests are rate limited per API key. Every error response is `{ "error": "code" }`. Common codes:

| Code | Status | Description |
| --- | --- | --- |
| `unauthorized` | 401 | The API key is missing, invalid, or disabled. |
| `insufficient_scope` | 403 | The key lacks the scope this endpoint requires. |
| `subscription_required` | 402 | No active plan. |
| `insufficient_credits` | 402 | Not enough credits for this request. |
| `not_found` | 404 | The resource does not exist or isn't yours. |
| `job_not_deletable` | 409 | Only failed runs can be deleted. |
| `invalid_request` | 400 | The request body or query failed validation. |
| `rate_limited` | 429 | Too many requests — see `Retry-After`. |
| `internal_error` | 500 | A bug on our side. Retrying will not help. |
| `*_unavailable` | 503 | Temporarily unavailable. Retry with backoff. |

**What the status tells you to do.** `4xx` means the request itself needs to change — resending it unchanged will fail the same way. `500` means we broke something and retrying will not help. `503` means it is worth trying again shortly: a dependency was slow or briefly unreachable, and nothing about your request was wrong.

Rate limits are per API key and depend on your plan. Throttled requests return `429` with a `Retry-After` header in seconds. Every authenticated response carries `X-RateLimit-Limit` and `X-RateLimit-Remaining`, so you can pace yourself without hitting the wall first.
