The CommodityAI API uses conventional HTTP response codes to indicate the success or failure of API requests. Error responses include detailed information to help you diagnose and resolve issues.
HTTP Status Codes
Resource created successfully (e.g., new record, uploaded document)
Request succeeded with no response body (e.g., successful deletion)
Invalid request format, missing required fields, or validation errors
Invalid or missing API key
API key lacks required permissions for this operation
Requested resource does not exist or is not accessible
Request conflicts with current resource state (e.g., duplicate record)
Request body or uploaded file exceeds size limits
415 - Unsupported Media Type
Unsupported file format or content type
422 - Unprocessable Entity
Request format is valid but cannot be processed (e.g., source not yet processed)
500 - Internal Server Error
Unexpected server error - contact support if persistent
Temporary server issue - retry with exponential backoff
503 - Service Unavailable
Server temporarily overloaded or under maintenance
All error responses follow a consistent JSON structure:
{
"error": "validation_failed",
"message": "One or more fields failed validation",
"details": {
"field_errors": {
"amount": ["must be a positive number"],
"due_date": ["must be a valid date in YYYY-MM-DD format"]
}
},
"request_id": "req_123e4567-e89b-12d3-a456-426614174000",
"timestamp": "2024-01-22T15:30:00Z"
}
Error Response Fields
Machine-readable error code for programmatic handling
Human-readable error description
Additional error information (varies by error type)
Unique identifier for this request (useful for support)
ISO 8601 timestamp when the error occurred
Common Error Types
Authentication Errors
Invalid API Key
{
"error": "invalid_api_key",
"message": "The provided API key is invalid or has been revoked",
"request_id": "req_123"
}
Missing API Key
{
"error": "missing_api_key",
"message": "No API key provided in Authorization header",
"request_id": "req_456"
}
Validation Errors
Missing Required Fields
{
"error": "validation_failed",
"message": "Required fields are missing",
"details": {
"missing_fields": ["invoice_number", "amount"],
"field_errors": {
"invoice_number": ["This field is required"],
"amount": ["This field is required"]
}
},
"request_id": "req_789"
}
Invalid Field Values
{
"error": "validation_failed",
"message": "Field validation failed",
"details": {
"field_errors": {
"amount": ["must be a positive number", "cannot exceed 999999.99"],
"currency": ["must be one of: USD, EUR, GBP"],
"due_date": ["must be a future date"]
}
},
"request_id": "req_101"
}
Resource Errors
Not Found
{
"error": "resource_not_found",
"message": "The requested record was not found",
"details": {
"resource_type": "record",
"resource_id": "rec_nonexistent"
},
"request_id": "req_112"
}
Duplicate Resource
{
"error": "duplicate_resource",
"message": "A record with this primary key already exists",
"details": {
"conflicting_fields": {
"invoice_number": "INV-2024-001"
},
"existing_record_id": "rec_existing_123"
},
"request_id": "req_131"
}
File Upload Errors
File Too Large
{
"error": "file_too_large",
"message": "File size exceeds maximum limit",
"details": {
"file_size": 52428800,
"max_size": 52428800,
"file_type": "pdf"
},
"request_id": "req_415"
}
Unsupported File Type
{
"error": "unsupported_file_type",
"message": "File type not supported for processing",
"details": {
"file_type": "txt",
"supported_types": ["pdf", "jpg", "jpeg", "png", "xlsx", "xls"]
},
"request_id": "req_416"
}
Processing Errors
Source Not Processed
{
"error": "source_not_processed",
"message": "Source document has not completed processing",
"details": {
"source_id": "src_123",
"current_status": "processing",
"estimated_completion": "2024-01-22T15:35:00Z"
},
"request_id": "req_422"
}
Processing Failed
{
"error": "processing_failed",
"message": "Document processing failed",
"details": {
"source_id": "src_456",
"failure_stage": "ocr",
"failure_reason": "OCR_QUALITY_TOO_LOW",
"failure_message": "Text quality insufficient for reliable extraction"
},
"request_id": "req_500"
}
Rate Limit Errors
The API enforces both per-minute and daily rate limits. When you exceed these limits, you’ll receive a 429 Too Many Requests response. For more information on rate limits, see Rate Limits.
Per-Minute Limit Exceeded
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Maximum 100 requests per minute. Retry after 42 seconds."
}
}
Response Headers:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit-Minute: 100
X-RateLimit-Remaining-Minute: 0
X-RateLimit-Reset-Minute: 1705939260
Retry-After: 42
Daily Limit Exceeded
{
"error": {
"code": "rate_limit_exceeded",
"message": "Daily rate limit exceeded. Maximum 10000 requests per day. Retry after 6 hours."
}
}
Response Headers:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit-Day: 10000
X-RateLimit-Remaining-Day: 0
X-RateLimit-Reset-Day: 1705968000
Retry-After: 21600
Use the Retry-After header to determine when to retry your request. This header indicates the number of seconds to wait before making another request.
Error Handling Best Practices
Programmatic Error Handling
Structure your error handling to respond appropriately to different error types:
async function makeAPIRequest(url, options) {
try {
const response = await fetch(url, options);
if (!response.ok) {
const errorData = await response.json();
throw new APIError(response.status, errorData);
}
return await response.json();
} catch (error) {
if (error instanceof APIError) {
handleAPIError(error);
} else {
handleNetworkError(error);
}
throw error;
}
}
class APIError extends Error {
constructor(status, errorData) {
super(errorData.message);
this.status = status;
this.error = errorData.error;
this.details = errorData.details;
this.requestId = errorData.request_id;
}
}
function handleAPIError(error) {
switch (error.status) {
case 400:
if (error.error === 'validation_failed') {
showValidationErrors(error.details.field_errors);
}
break;
case 401:
redirectToLogin();
break;
case 404:
showNotFoundMessage();
break;
case 409:
if (error.error === 'duplicate_resource') {
showDuplicateResourceDialog(error.details);
}
break;
case 429:
handleRateLimit(error);
break;
case 500:
case 502:
case 503:
scheduleRetry(error);
break;
default:
showGenericError(error.message);
}
}
Retry Logic
Implement intelligent retry logic for transient errors:
async function retryableRequest(url, options, maxRetries = 3) {
const retryableErrors = [429, 500, 502, 503];
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await makeAPIRequest(url, options);
} catch (error) {
if (error instanceof APIError && retryableErrors.includes(error.status)) {
if (attempt === maxRetries) {
throw error; // Final attempt failed
}
const delay = calculateBackoffDelay(attempt, error.status);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error; // Non-retryable error
}
}
}
function calculateBackoffDelay(attempt, statusCode) {
if (statusCode === 429) {
// Rate limit: longer delay
return Math.min(60000, Math.pow(2, attempt) * 2000);
}
// Server errors: exponential backoff
return Math.min(10000, Math.pow(2, attempt) * 1000);
}
User-Friendly Error Messages
Transform technical error messages into user-friendly text:
function getUserFriendlyMessage(error) {
const messages = {
'validation_failed': 'Please check the highlighted fields and try again.',
'duplicate_resource': 'A record with this information already exists.',
'resource_not_found': 'The requested item could not be found.',
'file_too_large': 'The selected file is too large. Please choose a smaller file.',
'unsupported_file_type': 'This file type is not supported. Please use PDF, Excel, or image files.',
'rate_limit_exceeded': 'You\'re making requests too quickly. Please wait a moment and try again.',
'processing_failed': 'We couldn\'t process your document. Please try uploading it again.',
'source_not_processed': 'Your document is still being processed. Please wait a moment and try again.'
};
return messages[error.error] || 'An unexpected error occurred. Please try again.';
}
Logging and Monitoring
Log errors with sufficient detail for debugging:
function logError(error, context = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
level: 'error',
message: error.message,
error_code: error.error,
status_code: error.status,
request_id: error.requestId,
context: context,
stack_trace: error.stack
};
// Send to your logging service
console.error(JSON.stringify(logEntry));
// Report to error tracking (e.g., Sentry)
if (error.status >= 500) {
reportToErrorTracking(error, context);
}
}
Debugging Tips
Using Request IDs
Include the request_id when contacting support:
function reportIssue(error, userDescription) {
const supportData = {
request_id: error.requestId,
timestamp: error.timestamp,
error_code: error.error,
user_description: userDescription,
browser_info: navigator.userAgent
};
// Send to support system
submitSupportTicket(supportData);
}
Testing Error Scenarios
Test your error handling with intentional errors:
// Test validation errors
const invalidRecord = {
fields: {
amount: -100, // Invalid: negative amount
currency: 'INVALID' // Invalid: not in enum
}
};
// Test not found errors
const nonexistentId = '00000000-0000-0000-0000-000000000000';
// Test rate limiting (make many rapid requests)
Error Response Inspection
Examine full error responses during development:
fetch('https://app.commodityai.com/api/v1/sources/123e4567-e89b-12d3-a456-426614174000/records', {
headers: {
'Authorization': 'Bearer cai_live_your_api_key_here'
}
})
.then(response => {
if (!response.ok) {
return response.json().then(errorData => {
console.group('API Error Details');
console.log('Status:', response.status);
console.log('Headers:', Object.fromEntries(response.headers));
console.log('Error Data:', errorData);
console.groupEnd();
throw new APIError(response.status, errorData);
});
}
return response.json();
});
Always include the request_id from error responses when contacting support. This helps our team quickly locate and diagnose the specific issue.