All errors follow a consistent structure. Use HTTP status codes for broad categorization and the error body for specific handling.
Error Response Format
{
"error": {
"type": "invalid_request_error",
"message": "Parameter 'externalUserId' is required"
}
}| Field | Description |
|---|---|
type | Machine-readable error category for programmatic handling |
message | Human-readable description for developers (do not show to end users) |
HTTP Status Codes
| Code | Meaning | Action |
|---|---|---|
| 200 | Success (all operations) | Process the response |
| 400 | Bad Request | Fix your request parameters |
| 401 | Unauthorized | Check your API key |
| 403 | Forbidden | Check account authorization, balance, or query quota |
| 404 | Not Found | Resource doesn't exist or has been deleted |
| 409 | Conflict | Resolve the conflict (duplicate, locked, etc.) |
| 429 | Too Many Requests | Respect Retry-After header and slow down |
| 500 | Internal Server Error | Retry with backoff; contact support if persistent |
| 503 | Service Unavailable | Temporary failure (e.g. profile ensure); safe to retry with backoff |
Error Types
The table below lists common error types to illustrate the response format. It is not exhaustive — each endpoint may return additional, operation-specific error types. Refer to the individual endpoint reference for the complete list of errors that endpoint can return.
| Type | HTTP | Description |
|---|---|---|
invalid_request_error | 400 | Parameter validation or malformed request |
invalid_json | 400 | Request body is not valid JSON |
authentication_error | 401 | API key invalid or missing |
permission_error | 403 | Account is not authorized for this service |
insufficient_balance_error | 403 | Account balance is insufficient |
over_query_limit_error | 403 | Query limit exceeded or service validity period expired |
profile_not_found | 404 | Profile does not exist |
case_not_found | 404 | AML case does not exist or does not belong to your account |
alert_not_found | 404 | AML alert does not exist or does not belong to your account |
match_not_found | 404 | AML match does not exist or does not belong to your account |
profile_already_exists | 409 | Duplicate externalUserId |
profile_deleted | 409 | Profile has been deleted |
profile_unavailable | 409 | Profile state does not support this operation |
profile_locked | 409 | Profile locked by another process |
rate_limit_error | 429 | Rate limit exceeded |
api_error | 500 / 503 | Unexpected server error (500); temporary ensure / infrastructure failure (503). Distinguish by HTTP status. |
service_unavailable | 503 | Used by some Application paths for lock / infrastructure fail-closed (service temporarily unavailable, please retry later) |
Handling Strategies
Client Errors (4xx)
These indicate a problem with your request. Do not retry without fixing the issue.
if response.status_code == 400:
error = response.json()["error"]
log.warning(f"Bad request: {error['message']}")
# Fix the request parameters
elif response.status_code == 409:
error = response.json()["error"]
if error["type"] == "profile_already_exists":
# Handle duplicate: fetch existing profile instead
passServer Errors (5xx)
These indicate a server-side issue. Retry with exponential backoff.
import time
max_retries = 3
for attempt in range(max_retries):
response = make_request()
if response.status_code < 500:
break
time.sleep(2 ** attempt)Rate Limiting (429)
Respect the Retry-After header:
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
time.sleep(retry_after)Request ID
Every response includes an X-Request-Id header. Include this when contacting support for faster debugging:
X-Request-Id: req_abc123def456