Every error returns the same JSON shape. The HTTP status code tells you what happened.
All errors return {"error":{"code":"...","message":"..."}}. The code is a stable string. The message is human-readable and may change.
{
"error": {
"code": "RATE_LIMITED",
"message": "Daily quota exceeded."
}
}
| Status | Error code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 404 | NOT_FOUND | Resource does not exist. |
| 409 | CONFLICT | Resource already exists. |
| 422 | INVALID_INPUT | Validation failed. |
| 429 | RATE_LIMITED | Quota exceeded. Check Retry-After header. |
| 500 | INTERNAL | Internal server error. |
| 501 | NOT_IMPLEMENTED | Endpoint not implemented. |
429 responses include a Retry-After header in seconds. Wait that long before retrying.
HTTP/1.1 429 Too Many Requests
Retry-After: 300
{
"error": {
"code": "RATE_LIMITED",
"message": "Daily quota exceeded."
}
}
POST endpoints accept an Idempotency-Key header. Send a UUID; we deduplicate for 24 hours.
curl -X POST https://api.crawlcrawl.com/v1/jobs \
-H "Authorization: Bearer crk_..." \
-H "Idempotency-Key: a0b1c2d3-e4f5-4678-8901-23456789abcd" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'