Skip to main content
The Catalog API uses standard HTTP status codes and returns detailed error information to help you troubleshoot issues quickly.

Error response format

All error responses follow a consistent structure:
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description",
    "request_id": "req_..."
  }
}
Additional fields may be included at the root level for specific error types. For example, validation errors may include additional context:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "One or more URLs have invalid format",
    "request_id": "req_abc123"
  },
  "invalid_urls": [
    "Index 0: https://invalid-url - invalid format"
  ]
}
All error responses include a Request-ID header that matches the request_id in the response body. Use this ID when contacting support.

HTTP status codes

Status CodeMeaningDescription
200SuccessRequest completed successfully
400Bad RequestInvalid request parameters or malformed JSON
401UnauthorizedMissing or invalid API key
402Payment RequiredInsufficient credits to complete the request
403ForbiddenAPI key lacks required permissions
404Not FoundRequested resource does not exist
409ConflictResource state conflict (e.g., operation already in progress)
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error occurred

Error codes reference

Authentication errors

CodeHTTP StatusDescriptionResolution
UNAUTHORIZED401Missing or invalid API keyVerify your API key is correct and included in the x-api-key header
FORBIDDEN403API key access deniedContact support to verify your API key permissions

Request validation errors

CodeHTTP StatusDescriptionResolution
INVALID_REQUEST400Invalid request format or missing required fieldsCheck the request format and ensure all required parameters are included
VALIDATION_ERROR400Request format is valid but values are invalidCheck parameter values against endpoint requirements (e.g., URL format, array length limits)

Resource errors

CodeHTTP StatusDescriptionResolution
NOT_FOUND404Resource not foundVerify the endpoint URL and any resource IDs (e.g., execution IDs)
CONFLICT409Resource state conflictThe requested operation conflicts with the current state (e.g., trying to cancel an execution that’s already stopped)

Billing errors

CodeHTTP StatusDescriptionResolution
INSUFFICIENT_CREDITS402Not enough credits to complete the requestAdd credits to your account or enable auto top-up. The response includes credits_needed and credits_remaining fields.

Rate limiting errors

CodeHTTP StatusDescriptionResolution
RATE_LIMITED429Too many requests in a short windowRespect Retry-After header, pace requests using RateLimit-Remaining, and back off (exponential recommended). Contact support if you need higher limits.

Server errors

CodeHTTP StatusDescriptionResolution
INTERNAL_ERROR500Unexpected server errorRetry the request; if persistent, contact support with the request_id

Handling errors

Basic error handling

const response = await fetch('https://api.getcatalog.ai/v1/search', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({ query: 'black dress' })
});

if (!response.ok) {
  const error = await response.json();
  const requestId = response.headers.get('Request-ID') || error.error?.request_id;
  
  switch (response.status) {
    case 401:
      console.error('Authentication failed:', error.error.message);
      break;
    case 402:
      console.error('Insufficient credits:', error.error.message);
      if (error.credits_needed && error.credits_remaining) {
        console.error(`Need ${error.credits_needed}, have ${error.credits_remaining}`);
      }
      break;
    case 403:
      console.error('Access forbidden:', error.error.message);
      break;
    case 404:
      console.error('Resource not found:', error.error.message);
      break;
    case 409:
      console.error('Conflict:', error.error.message);
      break;
    case 429:
      const retryAfter = response.headers.get('Retry-After');
      console.error('Rate limited. Retry after:', retryAfter);
      break;
    case 500:
      console.error('Server error:', error.error.message);
      console.error('Request ID:', requestId);
      break;
    default:
      console.error('Request failed:', error);
  }
  return;
}

const data = await response.json();

Retry with exponential backoff

For transient errors (402, 429, 500), implement exponential backoff:
async function requestWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);
    
    if (response.ok) {
      return response.json();
    }
    
    const error = await response.json();
    
    // Don't retry client errors (except 402 for credits)
    if (response.status === 402) {
      // Insufficient credits - don't retry, user needs to add credits
      throw new Error(`Insufficient credits: ${error.error.message}`);
    }
    
    if (response.status === 429 || response.status >= 500) {
      const retryAfter = response.headers.get('Retry-After');
      const delay = retryAfter 
        ? parseInt(retryAfter) * 1000 
        : Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      
      console.log(`Retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }
    
    // Non-retryable error
    throw new Error(`Request failed: ${response.status} - ${error.error.message}`);
  }
  
  throw new Error('Max retries exceeded');
}

Error response examples

Insufficient Credits (402)

{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Insufficient credits",
    "request_id": "req_01HZ..."
  },
  "credits_needed": 100,
  "credits_remaining": 50
}

Validation Error with Additional Context (400)

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "One or more URLs have invalid format",
    "request_id": "req_01HZ..."
  },
  "invalid_urls": [
    "Index 0: https://invalid-url - invalid format",
    "Index 2: not-a-url - invalid format"
  ]
}

Not Found (404)

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Execution not found",
    "request_id": "req_01HZ..."
  }
}

Conflict (409)

{
  "error": {
    "code": "CONFLICT",
    "message": "Crawl is currently stopping for example.com. Please wait.",
    "request_id": "req_01HZ..."
  }
}

Logical errors (200 OK responses)

Some endpoints return 200 OK even when individual items fail. Always check the response body:

Single product endpoint

{
  "product": null,
  "meta": {
    "found": false,
    "url": "https://example.com/invalid-product",
    "outcome": "VENDOR_NOT_SUPPORTED"
  }
}

Bulk products endpoint

{
  "products": [
    {
      "url": "https://valid-store.com/product",
      "success": true,
      "product": { ... }
    },
    {
      "url": "https://invalid-store.com/product",
      "success": false,
      "product": null,
      "outcome": "VENDOR_NOT_SUPPORTED"
    }
  ],
  "meta": {
    "total_requested": 2,
    "total_successful": 1,
    "total_failed": 1
  }
}
Always check meta.found or success fields in responses, even when the HTTP status is 200.

Need help?

If you encounter persistent errors or need assistance, contact our support team with:
  • The full error response (including the error object)
  • The request_id from the response (found in both the response body and Request-ID header)
  • Your request payload (with sensitive data redacted)
  • Timestamp of when the error occurred