Error handling & retries

4 min read

Handle API errors predictably with safe retry strategies.

Every error response follows the same structure and includes a clear error code you can match on programmatically.

Response format

Every error follows the same JSON structure. Match on the code field to handle specific cases in your code:

Error Response
JSON
{
  "success": false,
  "data": null,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Insufficient credits: 0 available, 1 required"
  },
  "metadata": {
    "requestId": "00bc3fa0-348f-4955-a1c1-3c7c91234568",
    "executionTimeMs": 3
  }
}

Note

Always include metadata.requestId when contacting support. It lets us trace your request instantly.

Error reference

Status What to do
422 Check your request body: a required field is missing or malformed (INVALID_LINKEDIN_URL is also returned for an invalid URL). For async endpoints, make sure a webhook URL is configured. On the search endpoints, see Search filter validation for the exact reason.
401 Check your API key: it may be missing, malformed, invalid, or expired. See Authentication.
402 No credits left. Paid lookups (INSUFFICIENT_CREDITS) and the free /check endpoints (NO_CREDITS) are both blocked until you top up. Purchase more or upgrade your plan.
403 Your workspace may be disabled (WORKSPACE_DISABLED), your key may lack a required permission, or the endpoint may require a paid plan. Upgrade to unlock live enrichment, email discovery, and more.
404 No result found for your query. Some endpoints still charge credits; see Not found pricing.
429 Rate limit reached. Wait and retry with backoff (see below).
451 This person has been removed per GDPR/CCPA. Enrichment is permanently blocked.
503 Our service is temporarily unavailable. Retry with backoff (see below).
504 The lookup took too long and timed out. Retry with backoff (see below).

Search filter validation

The search endpoints validate every filter before running the query. A rejected request returns 422, costs 0 credits, and never reaches the database. This is deliberate: a filter we cannot honour would otherwise return an empty page that looks like a legitimate “no results”, and bill you for it.

error.details.errors always lists every problem found, so one round trip is enough to fix a whole payload.

error.code Meaning
UNKNOWN_FIELD The field does not exist. Unknown fields are rejected rather than ignored, because an ignored filter returns unfiltered results. details.suggestion gives the closest accepted field, details.acceptedFields lists them all.
INVALID_FILTER_VALUE The value is outside a closed list. This covers industry and country values plus the People Search currentFunction, currentSubFunction and currentSeniority taxonomies. details.suggestions proposes up to three valid values.
EMPTY_FILTER_VALUE A blank string, empty array or empty object was sent. Remove the field instead.
FILTER_VALUES_LIMIT_EXCEEDED A filter carries more than 20 values. Split it into searches of at most 20 values and merge the results.
INVALID_RANGE A min/max pair is negative, fractional, or inverted (min greater than max).
INCOMPLETE_RANGE_FILTER followersCount used as the only filter requires both bounds.
INVALID_PAGINATION perPage, or page on Company Search, is not a whole number.
PAGINATION_LIMIT_EXCEEDED perPage is above 100, or page is above 50 on Company Search. details carries field and max.
INVALID_ID_FORMAT currentCompanyId is not a com_ identifier returned by our API.
INVALID_DATE_FORMAT maxDataAgeDate is not an ISO 8601 date-time.
CONTRADICTORY_FILTERS The same value appears in an included and excluded filter, which can never match.
NO_FILTER_PROVIDED The body carries pagination only: cursor / perPage for People Search or page / perPage for Company Search.
INVALID_SEARCH_FILTERS Several of the above at once. Read details.errors.
Response
Response
{
  "success": false,
  "data": null,
  "error": {
    "code": "INVALID_FILTER_VALUE",
    "message": "\"Technology\" is not an accepted value for \"industry\". Did you mean \"Technology, Information and Internet\", \"Technology, Information and Media\"? This filter is matched exactly, so an unlisted value can never return a result.",
    "details": {
      "field": "industry",
      "value": "Technology",
      "reason": "not_in_accepted_values",
      "suggestions": ["Technology, Information and Internet", "Technology, Information and Media"],
      "documentation": "https://docs.reversecontact.com/guides/industries"
    }
  }
}

Values are compared tolerantly before being rejected: case, surrounding spaces, accents and & versus and are normalised for you. Only a genuinely unknown value is refused.

When to retry

These statuses are safe to retry with exponential backoff:

  • 429 you’ve hit a rate limit. Wait for the reset window and retry.
  • 503 temporary service issue. Retry after a short delay.
  • 504 the lookup timed out before completing. Retry after a short delay.

All other errors require a change to your request or configuration before retrying.

Wait a little longer between each retry to give the server time to recover:

Retry delay
const delay = Math.min(1000 * 2 ** attempt, 15000)
const jitter = Math.floor(Math.random() * 250)
await new Promise((r) => setTimeout(r, delay + jitter))
delay = min(1 * 2 ** attempt, 15)
jitter = random.uniform(0, 0.25)
time.sleep(delay + jitter)

Credits and errors

All error responses (401, 402, 403, 422, 429, 451, 503, 504) cost 0 credits.

The 404 status is the only exception: some endpoints charge credits when the lookup was performed but no result was found. See Not found pricing for the full breakdown.

Previous

Which endpoint should I use?

Next

Rate limits & credits