Rate limits and errors
Limits are measured in weighted units, and every failure returns a stable code you can branch on.
Default limits
| Limit | Default | Applies to |
|---|---|---|
| Hourly budget | 300 units | Each key, and each IP |
| Burst | 60 units a minute | Each key, and each IP |
| Data API calls | 1,000 a day, resets at 00:00 UTC | Each account |
| Uploads at once | 2 | Each key |
| File size | 2 GB, or less where a tool says so | Each file |
| Files in a batch | 500 | Each request |
The headers are the live answer
These are defaults, and limits can change. Trust
X-RateLimit-Limit over this table. A data API call counts toward the daily quota even if the provider then blocks it.Request weights
Each request spends units according to its cost. Each tool page shows its own weight.
| Route | Units |
|---|---|
POST /v1/tools/:slug | 2 (20 on AI-worker tools) |
POST /v1/tools/:slug/jobs | 2 (20 on AI-worker tools) |
POST /v1/seo-tools/:slug | 2 |
GET /v1/apis/:slug | 2 |
POST /v1/pipeline | 3 |
POST /v1/batches | 5 |
POST /v1/jobs/:id/continue | 2 |
GET /v1/tools, GET /v1/formats | Not counted |
Response headers
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Units in the hourly budget |
X-RateLimit-Remaining | Units left this hour |
X-RateLimit-Reset | Unix time the hourly window resets |
X-RateLimit-Weight | Units this request spent |
Retry-After | Seconds to wait, sent on a fair-use 429 |
The daily data API limit reports its wait in details.retryAfterSeconds instead of a header.
Error format
Errors are JSON with a code and a readable message. Branch on the code, because messages can change. step names the failing pipeline step and details carries extras.
429 Too Many Requests
{
"code": "RATE_LIMITED",
"message": "Daily API quota reached for this account.",
"details": {
"limit": 1000,
"retryAfterSeconds": 3600
}
}Planned data APIs are the one exception. They answer 501 with {"error":"not_implemented","status":"planned"}.
Error codes
| Code | HTTP | Meaning |
|---|---|---|
INVALID_INPUT | 400 | A parameter is missing or malformed. |
OPTION_INVALID | 400 | An option failed the tool's schema, or is unknown. |
PIPELINE_INCOMPATIBLE | 400 | A step cannot take the previous step's output. |
TOO_MANY_FILES | 400 | More files than the tool accepts. |
UNAUTHENTICATED | 401 | The key is missing, invalid, expired or revoked. |
FORBIDDEN | 403 | The key lacks the scope, or the account is suspended. |
NOT_FOUND | 404 | No such route, data API or owned resource. |
TOOL_UNKNOWN | 404 | No tool has this slug. |
JOB_NOT_FOUND | 404 | The job does not exist. |
CANCELLED | 409 | The job was cancelled. |
CONFLICT | 409 | The request conflicts with existing state. |
JOB_EXPIRED | 410 | The job passed its 24-hour limit. |
JOB_DELETED | 410 | The job was deleted. |
FILE_TOO_LARGE | 413 | A file is over the limit for this tool. |
UNSUPPORTED_FORMAT | 415 | The tool does not accept this file type. |
ENCRYPTED_INPUT | 422 | The input is password-protected. |
CORRUPT_INPUT | 422 | The input could not be read. |
PDF_BOMB | 422 | The PDF looks like a decompression bomb. |
ZIP_BOMB | 422 | The archive looks like a decompression bomb. |
OUT_OF_MEMORY | 422 | The job ran out of memory. |
ENGINE_ERROR | 422 | The engine failed. details.code has the reason. |
RATE_LIMITED | 429 | A rate limit, the daily quota or the upload slots ran out. |
INTERNAL | 500 | Something failed on our side. |
OUTPUT_INVALID | 500 | The tool produced an output that failed checks. |
SERVICE_UNAVAILABLE | 503 | An upstream provider blocked or refused the request. |
TOOL_DISABLED | 503 | The tool is switched off for now. |
TIMEOUT | 504 | The engine or provider took too long. |

