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

CodeMeaningCommon causes
200OKRequest succeeded.
400Bad RequestInvalid parameters (e.g. non-integer product_id, missing required field, invalid enum value).
401UnauthorizedMissing, invalid, or expired API key.
402Payment RequiredInsufficient credits for a billable operation.
403ForbiddenKey lacks required scope, or API access is disabled for your team.
404Not FoundResource not found: product doesn't exist, prospect has no cached data, or company metadata not ready.
409ConflictDealMap generation already in progress for this prospect. Wait and retry.
410GoneThe 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).
429Too Many RequestsRate limit exceeded. Check X-RateLimit-Reset header.
500Internal Server ErrorAn unexpected error occurred. Details are not exposed to clients for security.
502Bad GatewayThe backend service returned a malformed or unexpected response.
503Service UnavailableThe product's prospect projection is unavailable (rebuilding or stale after a keywords edit) — see below — or a downstream service is temporarily down.
504Gateway TimeoutThe 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 the Retry-After header (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-After is on the order of an hour.
Error Handling | DealSurface Docs