Error Reference
The CrawlKit API uses standard HTTP status codes and returns structured JSON error responses. This guide covers the error format, all status codes, error codes, and how to handle errors in your code.
Error Response Format
All error responses follow a consistent JSON structure:
{
"error": "validation_error",
"message": "The 'url' field must be a valid URL",
"details": {
"field": "url",
"value": "not-a-url",
"constraint": "Must start with http:// or https://"
},
"request_id": "req_a1b2c3d4e5f6"
}
| Field | Type | Description |
|---|---|---|
error |
string | Machine-readable error code (e.g., validation_error, not_found) |
message |
string | Human-readable description of the error |
details |
object | null | Additional context, varies by error type. May include field names, constraints, or upstream error details. |
request_id |
string | Unique identifier for the request. Include this when contacting support. |
HTTP Status Codes
| Status | Name | Description | Retryable |
|---|---|---|---|
400 |
Bad Request | The request body or query parameters are malformed or missing required fields. | No |
401 |
Unauthorized | The API key is missing, invalid, or expired. Check the Authorization header. |
No |
403 |
Forbidden | Your account does not have permission for this action. This may be a tier restriction (e.g., Free tier trying to access a Pro endpoint) or a missing permission. | No |
404 |
Not Found | The requested resource does not exist. Check the ID or path. | No |
422 |
Unprocessable Entity | The request is syntactically valid but semantically incorrect. For example, a URL that cannot be reached or a dataset column type mismatch. | No |
429 |
Too Many Requests | Rate limit exceeded. See Rate Limits for retry strategies. | Yes |
500 |
Internal Server Error | An unexpected error occurred on the server. These are automatically logged. If persistent, contact support with the request_id. |
Yes |
503 |
Service Unavailable | The service is temporarily unavailable, usually during maintenance or high load. Retry with exponential backoff. | Yes |
Error Codes
The error field in the response body provides a machine-readable error code. Use these for programmatic error handling.
| Error Code | HTTP Status | Description |
|---|---|---|
validation_error |
400 | Request body or parameters failed validation. Check the details object for the specific field and constraint. |
invalid_json |
400 | The request body is not valid JSON. |
missing_field |
400 | A required field is missing from the request. |
invalid_api_key |
401 | The API key in the Authorization header is not recognized. |
expired_token |
401 | The JWT token has expired. Refresh the token and retry. |
missing_auth |
401 | No Authorization header was provided. |
insufficient_tier |
403 | Your subscription tier does not include access to this endpoint. Upgrade at crawlkit.app/settings/billing. |
insufficient_permission |
403 | Your account role does not have the required permission for this action. |
quota_exceeded |
403 | You have exceeded a usage quota (e.g., daily SEO checks, weekly audits). Resets at the next period boundary. |
not_found |
404 | The resource (dataset, spider, crawl job, etc.) was not found. |
url_unreachable |
422 | The URL provided could not be crawled. It may be blocked by robots.txt, return a non-200 status, or be unreachable. |
ssrf_blocked |
422 | The URL points to a private or internal network address. CrawlKit blocks SSRF attempts for security. |
invalid_selector |
422 | A CSS or XPath selector in a spider definition is invalid. |
rate_limit_exceeded |
429 | You have exceeded your per-minute or per-hour rate limit. Wait for the Retry-After period. |
internal_error |
500 | An unexpected internal error. Contact support with the request_id. |
service_unavailable |
503 | The service is temporarily unavailable. Retry with backoff. |
upstream_timeout |
503 | An upstream service (GSC, enrichment provider, etc.) timed out. Retry after a delay. |
Handling Errors in Code
JavaScript / TypeScript
class CrawlKitError extends Error {
constructor(
public status: number,
public code: string,
message: string,
public requestId: string,
public details?: Record<string, unknown>
) {
super(message);
this.name = "CrawlKitError";
}
get isRetryable(): boolean {
return [429, 500, 503].includes(this.status);
}
}
async function crawlkitFetch(path: string, options: RequestInit = {}) {
const response = await fetch(`https://api.crawlkit.app/api/v1${path}`, {
...options,
headers: {
"Authorization": `Bearer ${process.env.CRAWLKIT_API_KEY}`,
"Content-Type": "application/json",
...options.headers,
},
});
if (!response.ok) {
const body = await response.json();
throw new CrawlKitError(
response.status,
body.error,
body.message,
body.request_id,
body.details
);
}
return response.json();
}
// Usage
try {
const result = await crawlkitFetch("/check-url", {
method: "POST",
body: JSON.stringify({ url: "https://crawlkit.app" }),
});
console.log(result);
} catch (err) {
if (err instanceof CrawlKitError) {
if (err.code === "insufficient_tier") {
console.error("Upgrade required:", err.message);
} else if (err.isRetryable) {
console.error("Transient error, will retry:", err.message);
} else {
console.error(`API error [${err.code}]: ${err.message}`);
}
}
}
Python
import requests
class CrawlKitError(Exception):
def __init__(self, status: int, code: str, message: str,
request_id: str, details: dict = None):
super().__init__(message)
self.status = status
self.code = code
self.request_id = request_id
self.details = details
@property
def is_retryable(self) -> bool:
return self.status in (429, 500, 503)
def crawlkit_fetch(path: str, method: str = "GET", json_data: dict = None):
url = f"https://api.crawlkit.app/api/v1{path}"
headers = {
"Authorization": f"Bearer {os.environ['CRAWLKIT_API_KEY']}",
"Content-Type": "application/json",
}
response = requests.request(method, url, headers=headers, json=json_data)
if not response.ok:
body = response.json()
raise CrawlKitError(
status=response.status_code,
code=body.get("error", "unknown"),
message=body.get("message", "Unknown error"),
request_id=body.get("request_id", ""),
details=body.get("details"),
)
return response.json()
# Usage
try:
result = crawlkit_fetch("/check-url", "POST", {"url": "https://crawlkit.app"})
print(result)
except CrawlKitError as err:
if err.code == "insufficient_tier":
print(f"Upgrade required: {err}")
elif err.is_retryable:
print(f"Transient error, will retry: {err}")
else:
print(f"API error [{err.code}]: {err}")
Getting Help
If you encounter persistent errors or unexpected behavior:
- Include the
request_idfrom the error response when contacting support. - Check the status page for any ongoing incidents.
- Review the API Reference for the specific endpoint's expected request format.
- Email support@crawlkit.app with your request ID, the endpoint, and the full error response.