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

Resource-specific errors (orders, blacklists, authentication) return a flat JSON object:

{
  "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

Error Format Variations

The Enrichment Order, Blacklist, and authentication errors documented below use the flat format shown above, which matches the actual API responses. The general HTTP status sections (400, 401, 403, etc.) further down this page show a nested format with an error wrapper object -- these are illustrative examples for generic error handling patterns.


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

Enrichment Order Errors

These errors are specific to the Enrichment Order API.

Order Not Found (404)

Returned when the order ID does not exist or belongs to a different organization.

{
  "code": "ORDER_NOT_FOUND",
  "message": "Order with identifier 00000000-0000-0000-0000-000000000000 not found",
  "resource_type": "Order",
  "identifier": "00000000-0000-0000-0000-000000000000",
  "order_id": "00000000-0000-0000-0000-000000000000"
}

Resolution: Verify the order_id from your create order response. Orders are scoped to your organization.

Order Validation Errors (422)

Order creation uses Pydantic validation. Errors follow this format:

{
  "detail": [
    {
      "type": "error_type",
      "loc": ["body", "field_name"],
      "msg": "Human-readable message",
      "input": "the_invalid_value"
    }
  ]
}

Common validation errors:

Error loc Cause Fix
too_short ["body", "companies"] Empty companies list Provide at least 1 company
enum ["body", "features", 0] Invalid feature name Use: COMPANY_DATA, CONTACTS, TECHNOLOGIES, JOBS, HANDELSREGISTER, ANALYSIS, EMAIL_DISCOVERY, EMAIL_GENERATION
missing ["body", "companies", 0, "company_name"] Company without a name Every company needs company_name
value_error ["body"] Duplicate features Remove duplicates from features array
value_error ["body"] Both profile_id and profile_name Provide one or the other, not both

Insufficient Permissions (403)

Returned when your API key lacks the required scopes.

{
  "code": "AUTHORIZATION_ERROR",
  "message": "Insufficient permissions. Required scopes: orders:write",
  "required_scopes": ["orders:write"],
  "user_scopes": ["orders:read"]
}

Resolution: Create a new API key with both orders:read and orders:write scopes in Settings > Developer > API Keys.


Blacklist Errors

These errors are specific to the Blacklist Management API.

Blacklist Not Found (404)

Returned when the blacklist ID does not exist or belongs to a different organization.

{
  "code": "BLACKLIST_NOT_FOUND",
  "message": "Blacklist with identifier 00000000-0000-0000-0000-000000000000 not found",
  "resource_type": "Blacklist",
  "identifier": "00000000-0000-0000-0000-000000000000",
  "blacklist_id": "00000000-0000-0000-0000-000000000000"
}

Resolution: Verify the blacklist_id. Blacklists are scoped to your organization.

Blacklist Access Denied (403)

Returned when you attempt to access a blacklist that belongs to a different organization.

{
  "code": "BLACKLIST_ACCESS_DENIED",
  "message": "Access denied to blacklist 00000000-0000-0000-0000-000000000000",
  "blacklist_id": "00000000-0000-0000-0000-000000000000"
}

Resolution: You can only manage blacklists within your own organization. Verify the blacklist ID.

Blacklist Validation Errors (422)

Blacklist operations use Pydantic validation. Common errors:

Error loc Cause Fix
too_short ["body", "entries"] Empty entries list Provide at least 1 entry
too_long ["body", "entries"] More than 10,000 entries Split into multiple requests
missing ["body", "entries", 0, "company_name"] Entry without a name Every entry needs company_name
string_too_short ["body", "name"] Empty blacklist name Provide a non-empty name

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