The Catalog API uses standard HTTP status codes and returns detailed error information to help you troubleshoot issues quickly.
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 Code | Meaning | Description |
|---|
200 | Success | Request completed successfully |
400 | Bad Request | Invalid request parameters or malformed JSON |
401 | Unauthorized | Missing or invalid API key |
402 | Payment Required | Insufficient credits to complete the request |
403 | Forbidden | API key lacks required permissions |
404 | Not Found | Requested resource does not exist |
409 | Conflict | Resource state conflict (e.g., operation already in progress) |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Server-side error occurred |
Error codes reference
Authentication errors
| Code | HTTP Status | Description | Resolution |
|---|
UNAUTHORIZED | 401 | Missing or invalid API key | Verify your API key is correct and included in the x-api-key header |
FORBIDDEN | 403 | API key access denied | Contact support to verify your API key permissions |
Request validation errors
| Code | HTTP Status | Description | Resolution |
|---|
INVALID_REQUEST | 400 | Invalid request format or missing required fields | Check the request format and ensure all required parameters are included |
VALIDATION_ERROR | 400 | Request format is valid but values are invalid | Check parameter values against endpoint requirements (e.g., URL format, array length limits) |
Resource errors
| Code | HTTP Status | Description | Resolution |
|---|
NOT_FOUND | 404 | Resource not found | Verify the endpoint URL and any resource IDs (e.g., execution IDs) |
CONFLICT | 409 | Resource state conflict | The requested operation conflicts with the current state (e.g., trying to cancel an execution that’s already stopped) |
Billing errors
| Code | HTTP Status | Description | Resolution |
|---|
INSUFFICIENT_CREDITS | 402 | Not enough credits to complete the request | Add credits to your account or enable auto top-up. The response includes credits_needed and credits_remaining fields. |
Rate limiting errors
| Code | HTTP Status | Description | Resolution |
|---|
RATE_LIMITED | 429 | Too many requests in a short window | Respect Retry-After header, pace requests using RateLimit-Remaining, and back off (exponential recommended). Contact support if you need higher limits. |
Server errors
| Code | HTTP Status | Description | Resolution |
|---|
INTERNAL_ERROR | 500 | Unexpected server error | Retry 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