Skip to main content
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

200 - OK
Success
Request succeeded
201 - Created
Success
Resource created successfully (e.g., new record, uploaded document)
204 - No Content
Success
Request succeeded with no response body (e.g., successful deletion)
400 - Bad Request
Client Error
Invalid request format, missing required fields, or validation errors
401 - Unauthorized
Client Error
Invalid or missing API key
403 - Forbidden
Client Error
API key lacks required permissions for this operation
404 - Not Found
Client Error
Requested resource does not exist or is not accessible
409 - Conflict
Client Error
Request conflicts with current resource state (e.g., duplicate record)
413 - Payload Too Large
Client Error
Request body or uploaded file exceeds size limits
415 - Unsupported Media Type
Client Error
Unsupported file format or content type
422 - Unprocessable Entity
Client Error
Request format is valid but cannot be processed (e.g., source not yet processed)
429 - Too Many Requests
Client Error
Rate limit exceeded - see Rate Limits for handling
500 - Internal Server Error
Server Error
Unexpected server error - contact support if persistent
502 - Bad Gateway
Server Error
Temporary server issue - retry with exponential backoff
503 - Service Unavailable
Server Error
Server temporarily overloaded or under maintenance

Error Response Format

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

error
string
Machine-readable error code for programmatic handling
message
string
Human-readable error description
details
object
Additional error information (varies by error type)
request_id
string
Unique identifier for this request (useful for support)
timestamp
string
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.