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:

PreviousRate Limits NextIntegrations