View as Markdown

Errors

All v2 endpoints use conventional HTTP status codes and a consistent JSON envelope.

StatusMeaning
200Success
400Bad request - validation failed; the response body lists the offending fields
401Unauthorized - missing or invalid x-api-key header
402Payment required - not enough credits for the requested operation
408Request timeout - the lookup took too long; safe to retry
429Too many requests - demo account only; back off and retry later
5xxServer 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.codeMeaningExtra payload fields
unknown_fieldThe field does not exist on this endpointdid_you_mean (when close), allowed_fields
value_not_allowedA value is outside the field's closed vocabularyinvalid_values, allowed_values, enum
missing_fieldA required field is absent or empty
wrong_typeThe value has the wrong type (for example a string page)
out_of_rangeA number, length or list size is outside its bounds
invalid_formatA string does not match the expected format (id, URL, ...)
conflicting_fieldsTwo fields that mean the same thing were both sent
invalid_valueAny 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 (seniorities, industries, departments, ...), enum names it, and large ones such as industries are not inlined - read them from GET /v2/enums/<enum> instead.

The example below sends People Search a seniority outside the vocabulary and a misspelled company field:

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

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

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

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

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

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