# Errors All v2 endpoints use conventional HTTP status codes and a consistent JSON envelope. | Status | Meaning | | ------ | ----------------------------------------------------------------------------- | | `200` | Success | | `400` | Bad request - validation failed; the response body lists the offending fields | | `401` | Unauthorized - missing or invalid `x-api-key` header | | `402` | Payment required - not enough credits for the requested operation | | `408` | Request timeout - the lookup took too long; safe to retry | | `429` | Too many requests - demo account only; back off and retry later | | `5xx` | Server error - safe to retry with exponential backoff | Error responses share one envelope shape: `success` is `false` and `errors` is an array of `{ name, message }` objects (validation errors also carry a `payload` with the offending field). `data` is usually `null`, with one exception noted below. > **"Not found" results are HTTP `200`, not `404`** > > People Enrich, Company Enrich, and Email Finder return a well-formed `200` response with > `success: false` and `errors: [{ "name": "NOT_FOUND", ... }]` when a valid, well-formed request > simply finds no match - there is no dedicated `404` for this case. Email Finder's not-found > response also keeps a non-null `data` object (`email: null` plus whatever domain > information was resolved). Check `success` and `errors`, not the HTTP status, to detect a miss. ### 400 - Bad request Each failing field gets its own entry in `errors`, named `VALIDATION_ERROR`, with the offending field's full path in `payload.field`. `payload.code` says what kind of mistake it is - always check the code, not only the name: | `payload.code` | Meaning | Extra `payload` fields | | -------------------- | ---------------------------------------------------------- | -------------------------------------------- | | `unknown_field` | The field does not exist on this endpoint | `did_you_mean` (when close), `allowed_fields` | | `value_not_allowed` | A value is outside the field's closed vocabulary | `invalid_values`, `allowed_values`, `enum` | | `missing_field` | A required field is absent or empty | | | `wrong_type` | The value has the wrong type (for example a string page) | | | `out_of_range` | A number, length or list size is outside its bounds | | | `invalid_format` | A string does not match the expected format (id, URL, ...) | | | `conflicting_fields` | Two fields that mean the same thing were both sent | | | `invalid_value` | Any other rule the value breaks | | For `value_not_allowed`, `allowed_values` lists the whole vocabulary when it is short. When the vocabulary is one of the [enums](https://getprospect.com/api-docs/enums.md) (`seniorities`, `industries`, `departments`, ...), `enum` names it, and large ones such as `industries` are not inlined - read them from `GET /v2/enums/` instead. The example below sends People Search a seniority outside the vocabulary and a misspelled company field: ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-10-02T09:00:00.000Z" }, "errors": [ { "name": "VALIDATION_ERROR", "message": "filters.person_seniority.include: \"c_level\" is not an accepted value. Accepted: Owner, Partner, Chief Officer, VP, Director, Senior, Manager, Entry, Intern, Unpaid; the accepted values are listed at GET /v2/enums/seniorities", "payload": { "field": "filters.person_seniority.include", "code": "value_not_allowed", "received_value": [ "c_level" ], "received_type": "object", "invalid_values": [ "c_level" ], "allowed_values": [ "Owner", "Partner", "Chief Officer", "VP", "Director", "Senior", "Manager", "Entry", "Intern", "Unpaid" ], "enum": "seniorities", "constraints": { "isIn": "each value in include must be one of the following values: Owner, Partner, Chief Officer, VP, Director, Senior, Manager, Entry, Intern, Unpaid" } } }, { "name": "VALIDATION_ERROR", "message": "filters.company.domainz is not a field of this request - did you mean filters.company.domain?", "payload": { "field": "filters.company.domainz", "code": "unknown_field", "received_value": { "include": [ "stripe.com" ] }, "received_type": "object", "did_you_mean": "filters.company.domain", "allowed_fields": [ "id", "domain", "name", "linkedin_id" ], "constraints": { "whitelistValidation": "property domainz should not exist" } } } ] } ``` > **Filter-level rules surface as a plainer `400`** > > A handful of rules aren't expressed as per-field decorators - for example "at least one include > filter is required" on People Search. Those still return `400` / `VALIDATION_ERROR`, but as a > single `{ name, message }` entry with no `payload`. ### 401 - Unauthorized Returned when the `x-api-key` header is missing or invalid. ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-06-18T09:12:44.000Z" }, "errors": [ { "name": "UNAUTHORIZED", "message": "Unauthorized" } ] } ``` ### 402 - Payment required Returned once your workspace's credit balance reaches zero, before the request is even processed. The response also sets two headers: `X-Limit-Reached: true` and `X-Limit-Type: credits`. ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-07-25T14:02:31.000Z" }, "errors": [ { "name": "PAYMENT_REQUIRED", "message": "Workspace has reached its credit limit" } ] } ``` Some endpoints also run a narrower pre-flight check against the specific credit type a request would spend (for example People Search only checks email credits when you pass `enrich_email: true`), which fails the same way with a more specific message such as `"Insufficient email credits: have 0, need 3"`. ### 408 - Request timeout Email Finder and Email Verifier return `408` when the underlying lookup takes too long. Retry after a short delay. ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-07-25T14:02:31.000Z" }, "errors": [ { "name": "TIMEOUT", "message": "Email search is taking longer than expected. Please retry your request in a few minutes." } ] } ``` ### 429 - Too many requests Only applies to the shared demo account, capped at 1500 requests per hour per requesting IP address. Regular workspace API keys are not rate-limited. ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-07-25T14:02:31.000Z" }, "errors": [ { "name": "RATE_LIMIT", "message": "Demo user requests limit" } ] } ``` ### 5xx - Server error Returned when a downstream dependency is unavailable - for example, People Search and Company Search return `500` with this message when the search backend can't be reached. Safe to retry with exponential backoff. ```json { "success": false, "data": null, "metadata": { "timestamp": "2026-07-25T14:02:31.000Z" }, "errors": [ { "name": "INTERNAL_ERROR", "message": "Search backend unavailable" } ] } ``` An unexpected, unhandled error (rather than one the API explicitly detected and reported) returns the same `500` / `INTERNAL_ERROR` shape with a generic `"Internal server error"` message instead, so as not to leak internal details.