Skip to content

API Error Reference

Complete reference for all API error codes, meanings, and resolution strategies to handle errors gracefully in your integration.


🚨 Error Response Format

All API errors follow a consistent format:

{
  "code": "ENTITY_NOT_FOUND",
  "message": "Prospect with identifier ps_abc123 not found",
  "entity_type": "prospect",
  "identifier": "ps_abc123"
}

Error Response Fields

Field Description Always Present
code Machine-readable error code ✅ Yes
message Human-readable error description ✅ Yes
Additional fields Context-specific details (e.g., entity_type, identifier) ❌ Optional

🔴 Client Errors (4xx)

400 Bad Request

Invalid request syntax or parameters:

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "The request body is invalid",
    "details": {
      "validation_errors": [
        {
          "field": "email",
          "error": "Invalid email format",
          "provided": "not-an-email"
        },
        {
          "field": "qualification_score",
          "error": "Must be between 0 and 10",
          "provided": 15
        }
      ]
    }
  }
}

Common Causes & Solutions:

Error Code Cause Solution
INVALID_JSON Malformed JSON body Validate JSON syntax
MISSING_FIELD Required field absent Check API docs for required fields
INVALID_TYPE Wrong data type Ensure correct types (string, number, etc.)
INVALID_ENUM Invalid enum value Use only allowed values
INVALID_FORMAT Wrong format (date, email) Follow format specifications

Python Example - Handling Validation:

try:
    response = requests.post(url, json=data)
    response.raise_for_status()
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 400:
        error = e.response.json()['error']
        if error['code'] == 'INVALID_REQUEST':
            for validation_error in error['details']['validation_errors']:
                print(f"Fix {validation_error['field']}: {validation_error['error']}")

401 Unauthorized

Authentication required or failed:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required",
    "details": {
      "reason": "token_expired",
      "expired_at": "2024-01-20T09:00:00Z",
      "suggestion": "Refresh your access token"
    }
  }
}

Authentication Error Codes:

Code Description Resolution
TOKEN_EXPIRED Access token expired Refresh token or re-authenticate
TOKEN_INVALID Malformed or corrupted token Re-authenticate
TOKEN_REVOKED Token was revoked Re-authenticate with new credentials
NO_TOKEN Missing Authorization header Include Authorization: Bearer TOKEN
INSUFFICIENT_SCOPE Token lacks required permissions Request additional scopes

Auto-Retry Pattern:

def make_authenticated_request(url, data, retry=True):
    try:
        response = requests.post(url, json=data, headers=headers)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401 and retry:
            # Refresh token
            new_token = refresh_access_token()
            headers['Authorization'] = f'Bearer {new_token}'
            # Retry once
            return make_authenticated_request(url, data, retry=False)
        raise

403 Forbidden

Authorized but lacks permission:

{
  "error": {
    "code": "FORBIDDEN",
    "message": "You don't have permission to access this resource",
    "details": {
      "resource": "prospect_search",
      "resource_id": "ps_123",
      "required_permission": "prospect_search:write",
      "your_permissions": ["prospect_search:read"]
    }
  }
}

Permission Error Types:

Code Scenario Solution
RESOURCE_ACCESS_DENIED Not your resource Only access your own data
PLAN_LIMIT_EXCEEDED Feature not in plan Upgrade plan
TEAM_PERMISSION_REQUIRED Need team role Contact admin for access
API_DISABLED API access disabled Enable in settings

404 Not Found

Resource doesn't exist:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested resource was not found",
    "details": {
      "resource_type": "prospect",
      "resource_id": "pr_nonexistent",
      "suggestion": "The prospect may have been deleted or the ID is incorrect"
    }
  }
}

Not Found Scenarios:

Resource Common Causes How to Check
Prospect Deleted or wrong ID List all prospects first
Search Completed or expired Check search status
Task Already processed Verify task state
Export Expired download Request new export

409 Conflict

Request conflicts with current state:

{
  "error": {
    "code": "CONFLICT",
    "message": "The request conflicts with the current state",
    "details": {
      "conflict_type": "duplicate_entry",
      "existing_resource": {
        "type": "webhook",
        "id": "wh_123",
        "name": "CRM Sync"
      },
      "suggestion": "A webhook with this URL already exists"
    }
  }
}

Conflict Types:

Code Description Resolution
DUPLICATE_ENTRY Resource already exists Use existing or choose different identifier
STATE_CONFLICT Invalid state transition Check current state first
CONCURRENT_MODIFICATION Resource modified elsewhere Retry with latest version
RESOURCE_LOCKED Resource being processed Wait and retry

422 Unprocessable Entity

Valid syntax but semantic errors:

{
  "error": {
    "code": "UNPROCESSABLE_ENTITY",
    "message": "The request is valid but cannot be processed",
    "details": {
      "reason": "business_logic_violation",
      "specifics": "Cannot generate emails without contact information",
      "missing_data": ["contact.email", "contact.first_name"],
      "suggestion": "Run email discovery first"
    }
  }
}

Business Logic Errors:

Code Scenario Fix
MISSING_PREREQUISITES Required data absent Complete prior steps
INVALID_STATE_TRANSITION Workflow violation Follow correct sequence
BUSINESS_RULE_VIOLATION Rule check failed Adjust request parameters
INSUFFICIENT_DATA Not enough information Enrich data first

🔴 Server Errors (5xx)

500 Internal Server Error

Unexpected server error:

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred",
    "details": {
      "request_id": "req_abc123",
      "suggestion": "Please try again. If the problem persists, contact support with the request_id"
    }
  }
}

Recovery Strategy:

def resilient_request(url, data, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=data)
            if response.status_code >= 500:
                if attempt < max_retries - 1:
                    wait = (2 ** attempt) + random.random()
                    time.sleep(wait)
                    continue
            response.raise_for_status()
            return response.json()
        except Exception as e:
            if attempt == max_retries - 1:
                raise

resilient_request(url, data)

502 Bad Gateway

Upstream service error:

{
  "error": {
    "code": "BAD_GATEWAY",
    "message": "Upstream service unavailable",
    "details": {
      "service": "email_validation",
      "suggestion": "The email validation service is temporarily unavailable"
    }
  }
}

503 Service Unavailable

Service temporarily unavailable:

{
  "error": {
    "code": "SERVICE_UNAVAILABLE",
    "message": "Service temporarily unavailable",
    "details": {
      "reason": "maintenance",
      "expected_duration": "30m",
      "status_page": "https://status.openprospect.io"
    }
  }
}

504 Gateway Timeout

Request timeout:

{
  "error": {
    "code": "GATEWAY_TIMEOUT",
    "message": "The request timed out",
    "details": {
      "timeout_seconds": 30,
      "suggestion": "Large requests may take longer. Try with fewer records or use async endpoints"
    }
  }
}

🛠️ Error Handling Best Practices

1. Comprehensive Error Handler

class OpenProspectAPIError(Exception):
    def __init__(self, response):
        self.response = response
        error_data = response.json()  # Error object directly, not wrapped
        self.code = error_data.get('code', 'UNKNOWN')
        self.message = error_data.get('message', 'Unknown error')
        self.status_code = response.status_code

        super().__init__(self.message)

def handle_api_response(response):
    """Universal API response handler"""
    try:
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        error_code = e.response.json().get('code')

        # Handle specific errors
        if e.response.status_code == 401:
            if error_code == 'AUTHENTICATION_ERROR':
                # Invalid or expired API key - can't auto-refresh
                raise OpenProspectAPIError(e.response)
            else:
                raise OpenProspectAPIError(e.response)

        elif e.response.status_code == 503:
            # Handle service unavailable - retry with backoff
            return retry_with_backoff()

        elif e.response.status_code >= 500:
            # Retry server errors with backoff
            return retry_with_backoff()

        else:
            # Client errors (4xx) - don't retry
            raise OpenProspectAPIError(e.response)

2. Error Recovery Patterns

# Idempotent retry wrapper
@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=4, max=10),
    retry=retry_if_exception_type(requests.exceptions.RequestException)
)
def safe_api_call(endpoint, data):
    response = requests.post(endpoint, json=data)
    response.raise_for_status()
    return response.json()

# Circuit breaker pattern
class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=60):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = 'closed'  # closed, open, half-open

    def call(self, func, *args, **kwargs):
        if self.state == 'open':
            if time.time() - self.last_failure_time > self.recovery_timeout:
                self.state = 'half-open'
            else:
                raise Exception("Circuit breaker is open")

        try:
            result = func(*args, **kwargs)
            if self.state == 'half-open':
                self.state = 'closed'
                self.failure_count = 0
            return result
        except Exception as e:
            self.failure_count += 1
            self.last_failure_time = time.time()

            if self.failure_count >= self.failure_threshold:
                self.state = 'open'
            raise

3. Error Logging

import logging

logger = logging.getLogger('openprospect_api')

def log_api_error(error):
    """Log API errors with context"""
    logger.error(
        f"API Error: {error.code} - {error.message}",
        extra={
            'request_id': error.request_id,
            'error_code': error.code,
            'error_details': error.details,
            'status_code': error.response.status_code
        }
    )

    # Log to monitoring service
    if error.response.status_code >= 500:
        send_to_monitoring_service({
            'error': error.code,
            'request_id': error.request_id,
            'severity': 'high'
        })

📊 Common Error Scenarios

Webhook Delivery Errors

{
  "error": {
    "code": "WEBHOOK_DELIVERY_FAILED",
    "message": "Failed to deliver webhook",
    "details": {
      "webhook_id": "wh_123",
      "attempt": 3,
      "max_attempts": 5,
      "last_error": "Connection timeout",
      "next_retry": "2024-01-20T11:30:00Z",
      "suggestion": "Check your webhook endpoint is accessible"
    }
  }
}

AI Processing Errors

{
  "error": {
    "code": "AI_PROCESSING_FAILED",
    "message": "AI analysis failed",
    "details": {
      "reason": "insufficient_data",
      "missing_fields": ["company.description", "company.website_content"],
      "suggestion": "Ensure company has been crawled successfully"
    }
  }
}

Data Quality Errors

{
  "error": {
    "code": "DATA_QUALITY_ERROR",
    "message": "Data quality check failed",
    "details": {
      "issues": [
        {
          "field": "email",
          "issue": "invalid_format",
          "value": "notanemail"
        },
        {
          "field": "phone",
          "issue": "invalid_country_code",
          "value": "+00 123456"
        }
      ],
      "suggestion": "Fix data quality issues before proceeding"
    }
  }
}

🚀 Quick Reference

HTTP Status to Action Mapping

Status Action Retry?
400 Fix request ❌ No
401 Check API key ✅ Once
403 Check permissions ❌ No
404 Verify resource ❌ No
409 Resolve conflict ❌ No
422 Fix business logic ❌ No
500 Retry with backoff ✅ Yes
502 Retry with backoff ✅ Yes
503 Retry with backoff ✅ Yes
504 Use async endpoint ⚠️ Maybe

💡 Pro Tips

  1. Store Your API Key Securely: Use environment variables or secret management
  2. Don't Retry Client Errors (4xx): Fix the request instead
  3. Implement Backoff for Server Errors (5xx): Use exponential backoff
  4. Monitor Error Patterns: Track errors to identify integration issues
  5. Use Async for Large Operations: Avoid timeouts on long-running tasks

Need Help? Contact support at support@openprospect.io