Error Handling
Understand the error model used across all DealSurface API endpoints.
Error response format
Error responses are JSON objects with an error field describing the issue. Some errors include additional fields for context.
{
"error": "Human-readable error message"
}Status codes
| Code | Meaning | Common causes |
|---|---|---|
| 200 | OK | Request succeeded. |
| 400 | Bad Request | Invalid parameters (e.g. non-integer product_id, missing required field, invalid enum value). |
| 401 | Unauthorized | Missing, invalid, or expired API key. |
| 402 | Payment Required | Insufficient credits for a billable operation. |
| 403 | Forbidden | Key lacks required scope, or API access is disabled for your team. |
| 404 | Not Found | Resource not found: product doesn't exist, prospect has no cached data, or company metadata not ready. |
| 409 | Conflict | DealMap generation already in progress for this prospect. Wait and retry. |
| 410 | Gone | The request hit a /api/v1 path. v1 has been removed; the body names /api/v2 and the changelog. Update your base URL (and upgrade the MCP server to 2.0.0 if you use it). |
| 429 | Too Many Requests | Rate limit exceeded. Check X-RateLimit-Reset header. |
| 500 | Internal Server Error | An unexpected error occurred. Details are not exposed to clients for security. |
| 502 | Bad Gateway | The backend service returned a malformed or unexpected response. |
| 503 | Service Unavailable | The product's prospect projection is unavailable (rebuilding or stale after a keywords edit) — see below — or a downstream service is temporarily down. |
| 504 | Gateway Timeout | The backend did not respond within the timeout (60 seconds for lead/outreach generation). |
Error examples
400 Bad Request
Validation errors on enumerated inputs always name the accepted set — an unknown value is never ignored and never silently matches nothing.
{
"error": "Unknown lines_of_business value \"comercial_auto\". Accepted values: accident_health, aircraft, allied_lines, ... Did you mean \"commercial_auto\"?"
}402 Payment Required
{
"error": "Insufficient credits",
"code": "insufficient_credits",
"operation": "prospect_org_chart_annotation",
"required_credits": 10,
"remaining_credits": 0,
"period_ends_at": "2026-09-01T00:00:00Z"
}409 Conflict (generation in progress)
Returned by POST /dealmap/generate while another generation for the same prospect is in flight. Wait for the first request to finish, then read the result with GET /dealmap.
{
"error": "DealMap generation already in progress for this prospect"
}410 Gone (v1 removed)
Every /api/v1 path returns 410 permanently. This is not transient — do not retry; move to /api/v2.
{
"error": "The v1 API has been removed. Use https://dealsurface.com/api/v2 instead.",
"docs": "https://dealsurface.com/docs/api/changelog"
}429 Too Many Requests
{
"error": "Rate limit exceeded. Please try again later."
}503 Service Unavailable (projection unavailable)
GET /prospects, GET /prospects/:ticker, and GET /excerpts return this when the product's prospect data is not ready. It always means “not ready”, never “no prospects found” — in v1 the same states produced a misleading empty list.
{
"error": "projection_unavailable",
"reason": "rebuilding",
"retry_after_seconds": 30
}reason: "rebuilding"— a refresh is actively running. Worth polling; honor theRetry-Afterheader (typically ~30 seconds).reason: "stale_keywords"— the product's keywords were edited and the data has not been rebuilt for them yet. This waits on a pipeline run, so do not poll tightly;Retry-Afteris on the order of an hour.