Skip to main content

Error Handling

Robust error handling is essential for building reliable integrations. This guide covers error formats, common error codes, and strategies for graceful degradation.

2xx Success

Request completed successfully

4xx Client Error

Invalid request or authentication

5xx Server Error

Temporary server issues

Error Response Format

All API errors follow a consistent JSON structure with detailed information for debugging.

Error Response Structure
{
  "error": {
    "code": "validation_error",
    "message": "The request body contains invalid fields",
    "details": [
      {
        "field": "email",
        "code": "invalid_format",
        "message": "Email address is not valid"
      },
      {
        "field": "phone",
        "code": "missing_required",
        "message": "Phone number is required"
      }
    ],
    "request_id": "req_abc123xyz",
    "documentation_url": "https://docs.synaptis.io/errors/validation_error"
  }
}

HTTP Status Codes

StatusMeaningAction
200 OKRequest succeededProcess response data
201 CreatedResource createdProcess created resource
400 Bad RequestInvalid request formatFix request and retry
401 UnauthorizedInvalid or missing API keyCheck authentication
403 ForbiddenInsufficient permissionsCheck API key scopes
404 Not FoundResource doesn't existCheck resource ID
422 UnprocessableValidation failedFix validation errors
429 Too Many RequestsRate limit exceededWait and retry
500 Internal ErrorServer errorRetry with backoff
502 Bad GatewayUpstream errorRetry with backoff
503 Service UnavailableService temporarily downRetry with backoff

Error Codes

Specific error codes provide detailed information about what went wrong.

Authentication Errors

invalid_api_keyThe API key provided is invalid or has been revoked
expired_tokenThe access token has expired. Refresh using your refresh token
insufficient_scopeThe API key lacks required permissions for this endpoint

Validation Errors

validation_errorOne or more fields failed validation
invalid_formatField value doesn't match expected format
missing_requiredA required field is missing from the request

Lead Processing Errors

duplicate_leadA lead with this data already exists in the system
lead_rejectedLead failed quality checks and was rejected
no_matching_buyersNo buyers matched the lead's criteria
capacity_exceededAll matching buyers have reached their capacity limits

Retry Strategies

Implement intelligent retry logic to handle transient failures gracefully.

Retryable vs Non-Retryable

Only retry on 5xx errors and 429. Don't retry 4xx errors (except 429) as they indicate client issues.

Complete Retry Implementation
class APIClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.maxRetries = 3;
    this.baseDelay = 1000;
  }

  async request(endpoint, options = {}) {
    let lastError;
    
    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        const response = await fetch(`https://api.synaptis.io/v1${endpoint}`, {
          ...options,
          headers: {
            'Authorization': `Bearer ${this.apiKey}`,
            'Content-Type': 'application/json',
            ...options.headers
          }
        });

        // Don't retry client errors (except rate limiting)
        if (response.status >= 400 && response.status < 500 && response.status !== 429) {
          const error = await response.json();
          throw new APIError(error, response.status);
        }

        // Retry on rate limit
        if (response.status === 429) {
          const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
          await this.sleep(retryAfter * 1000);
          continue;
        }

        // Retry on server errors
        if (response.status >= 500) {
          throw new Error(`Server error: ${response.status}`);
        }

        return await response.json();
        
      } catch (error) {
        lastError = error;
        
        if (error instanceof APIError) throw error;
        
        if (attempt < this.maxRetries) {
          // Exponential backoff with jitter
          const delay = this.baseDelay * Math.pow(2, attempt);
          const jitter = delay * 0.2 * Math.random();
          await this.sleep(delay + jitter);
        }
      }
    }
    
    throw lastError;
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

Fallback Patterns

Design your integration to degrade gracefully when the API is unavailable.

Queue for later processing

Store failed requests in a queue and process them when the API recovers.

Use cached data

Return cached results when fresh data cannot be retrieved.

Circuit breaker pattern

Stop making requests after repeated failures to prevent cascade issues.

User-friendly error messages

Display helpful messages instead of exposing technical error details.