Skip to content

Integration guides

Error handling & retries

Updated

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:

JSON

Error Response

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

Error reference

StatusWhat to do
422Check 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.
401Check your API key: it may be missing, malformed, invalid, or expired. See Authentication.
402No 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.
403Your 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.
404No result found for your query. Some endpoints still charge credits; see Not found pricing.
429Rate limit reached. Wait and retry with backoff (see below).
451This person has been removed per GDPR/CCPA. Enrichment is permanently blocked.
503Our service is temporarily unavailable. Retry with backoff (see below).
504The 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.codeMeaning
UNKNOWN_FIELDThe 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_VALUEThe value is outside a closed list. This covers industry and country values plus the People Search currentFunction, currentSubFunction and currentSeniority taxonomies and the changedJobsWithinDays windows, as well as keywords without a single word. details.suggestions proposes up to three valid values.
EMPTY_FILTER_VALUEA blank string, empty array or empty object was sent. Remove the field instead.
FILTER_VALUES_LIMIT_EXCEEDEDA filter carries more than 20 values. Split it into searches of at most 20 values and merge the results.
INVALID_RANGEA min/max pair is negative, fractional, or inverted (min greater than max).
INCOMPLETE_RANGE_FILTERfollowersCount used as the only filter requires both bounds.
INVALID_PAGINATIONperPage, or page on Company Search, is not a whole number.
PAGINATION_LIMIT_EXCEEDEDperPage is above 100, or page is above 50 on Company Search. details carries field and max.
INVALID_ID_FORMATcurrentCompanyId is not a com_ identifier returned by our API.
INVALID_DATE_FORMATmaxDataAgeDate is not an ISO 8601 date-time.
CONTRADICTORY_FILTERSThe same value appears in an included and excluded filter, which can never match.
NO_FILTER_PROVIDEDThe body carries pagination only: cursor / perPage for People Search or page / perPage for Company Search.
INVALID_SEARCH_FILTERSSeveral of the above at once. Read details.errors.
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))

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.