Skip to content

MaintenanceThe former company search endpoint goes into maintenance on November 9Migration guide

Learn // Go to production

Error handling & retries

Updated
Go to production · Step 1 of 2

Last verified

Handle API errors predictably: the error response format, what each HTTP status means, search filter validation, and safe retry strategies.

When a request fails, the API returns an HTTP status and an error response with the same structure on every endpoint, including an 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
400The request is malformed: a JSON body that cannot be read, or a value the endpoint refuses, such as INVALID_IDEMPOTENCY_KEY, WEBHOOK_URL_UNREACHABLE (a webhookUrl that is not public HTTPS) or JOB_INVALID_INPUT (a filter or a cursor of the job history). Fix the request before you send it again.
422Check your request body: a required field is missing or malformed (INVALID_LINKEDIN_URL is also returned for an invalid URL). On the search endpoints, see Search filter validation for the exact reason. On Bulk enrichment, UNKNOWN_FIELD names a field the body or an item does not define, with details.suggestion and details.acceptedFields. With IDEMPOTENCY_KEY_REUSED, the Idempotency-Key was already used with a different body: use a new key.
401Check your API key: it may be missing, malformed, invalid, or expired (API_KEY_EXPIRED). See Authentication.
402Not enough credits. A paid lookup that costs more than your balance returns INSUFFICIENT_CREDITS, and the free /check endpoints return NO_CREDITS once fewer than 2 credits are left. Bulk enrichment returns BULK_JOB_INSUFFICIENT_CREDITS when your balance does not cover a single item. Purchase more or upgrade your plan.
403Your workspace may be disabled (WORKSPACE_DISABLED), your key may lack a required permission (INSUFFICIENT_PERMISSIONS), or your key reached its total credit limit (KEY_CREDIT_LIMIT_REACHED): edit the key in API Keys to add the permission, or to raise or reset the limit. Bulk enrichment returns BULK_JOB_PHONE_FINDER_DISABLED when it asks for phone numbers on a workspace where phone finding is not enabled, and Cancel a job returns JOB_STARTED_BY_A_PERSON for a job a person started in the app: stop it there.
404No result found for your query. Some endpoints still charge credits; see API pricing.
409With IDEMPOTENCY_KEY_IN_USE, a request with the same Idempotency-Key is still being processed: wait the Retry-After seconds, then retry with the same key to receive the first request's response. See Find Email. With JOB_RESULTS_NOT_READY, the job has not finished: follow it with Get a job and download the results once links.results is set. With JOB_NOT_CANCELLABLE, the job already ended: there is nothing to retry. With JOB_RESULTS_FORMAT_UNAVAILABLE, the results of the job were stored before NDJSON and reading by page existed: download them as CSV.
410The endpoint was retired. Call the endpoint named in error.details.alternative instead.
413The JSON body is over 25 MB (REQUEST_BODY_TOO_LARGE). Send a smaller body: retrying the same one will not help.
429Rate limit reached. Wait the number of seconds in error.details.retryAfter, then retry (see below). The limits of each plan are in Rate limits. With KEY_CREDIT_LIMIT_REACHED, the key reached its daily or monthly credit limit: see API key management.
451This person or company asked not to be processed under GDPR or CCPA. The lookup is permanently blocked.
500Unexpected error on our side. Retry with backoff (see below).
502A provider we rely on failed for this request. Retry with backoff (see below).
503Our service is temporarily unavailable. Retry with backoff (see below). With BULK_QUEUE_UNAVAILABLE, nothing was created: send the Bulk enrichment request again shortly, with the same Idempotency-Key.
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. Without this check, a filter we cannot honor would return an empty page that looks like a legitimate "no results".

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. A page sent to People Search or Company Search, which paginate with a cursor, suggests cursor.
INVALID_FILTER_VALUEThe value is outside a closed list. This covers industry and country values plus the People Search currentFunction, currentSubFunction and currentSeniority taxonomies, its contract types and the changedJobsWithinDays windows, as well as keywords or schoolName without a single word. details.suggestions proposes up to three valid values, or the eight contract types when none is close.
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 an include list into searches of at most 20 values and merge the results. Splitting does not work for an exclude... list, because each search only applies its own list: shorten it, or use a taxonomy filter such as excludeCurrentFunction. For countries, use location.area.
INVALID_RANGEA min/max pair is negative, fractional, inverted (min greater than max), or above the maximum of its filter, given in details.max, such as 2100 for the Company Search foundedYear.
INCOMPLETE_RANGE_FILTERfollowersCount used as the only filter requires both bounds.
INVALID_PAGINATIONperPage is not a whole number.
PAGINATION_LIMIT_EXCEEDEDperPage is above 100. details carries field and max.
INVALID_ID_FORMATcurrentCompanyId is not a com_ identifier returned by our API, or schoolId is not a sch_ identifier.
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), or maxDataAgeDate is the only filter of a Company Search, which it can narrow but not select on its own.
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 normalized for you. Only an unknown value is refused.

When to retry

These statuses are safe to retry, and they cost 0 credits:

  • 429: you've hit a rate limit. Wait the number of seconds in error.details.retryAfter, then retry. With KEY_CREDIT_LIMIT_REACHED, that wait runs until 00:00 UTC or the 1st of the month: pause the work rather than retrying in a loop.
  • 500, 502, 503 and 504: a temporary issue on our side or at a provider we rely on. Retry with exponential backoff (below), and stop after a few attempts, for example 5, then alert. Incidents are posted on status.reversecontact.com, where you can subscribe to updates by email, Slack, webhook or RSS.
  • 409 with IDEMPOTENCY_KEY_IN_USE: a request with the same Idempotency-Key is still being processed. Wait the Retry-After seconds, then retry with the same key. A 409 JOB_RESULTS_NOT_READY asks you to wait for the job instead, and a 409 JOB_NOT_CANCELLABLE or 409 JOB_RESULTS_FORMAT_UNAVAILABLE is final.

All other errors (400, 401, 402, 403, 404, 410, 413, 422, 451) require a change to your request, your key or your credit balance before retrying.

Find Email and Find Phone debit credits when their job is created, and Bulk enrichment reserves them when it creates its job. Before you retry one of them after a timeout or a dropped connection, make sure the request carries an Idempotency-Key: a retry with the same key returns the first response instead of starting a second lookup or job. See Find Email.

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))

Timeouts and aborted requests

Set your client timeout to at least 45 seconds on the search and count endpoints, and 60 seconds on prompt search. When a search runs out of time, we answer with a JSON 504 before those limits, and it costs 0 credits. A shorter client timeout can cut the connection just before that answer arrives.

Closing the connection does not cancel the request. It keeps running on our side and can still be billed, even though you never receive the page. A retry after an abort can then bill the same page twice, so raise a timeout that fires too early rather than aborting and retrying.

Credits and errors

All error responses (401, 402, 403, 409, 410, 413, 422, 429, 451, 500, 502, 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 API pricing for the full breakdown.

FAQ

Which Reverse Contact API errors are safe to retry?

429 (rate limit reached) is safe to retry after the number of seconds in error.details.retryAfter. 500, 502, 503 and 504 (a temporary issue on our side or at a provider) are safe to retry with exponential backoff, for example a delay that doubles from 1 second up to 15 seconds, plus up to 250 ms of random jitter, for a few attempts. All other errors require a change to your request, your key or your credit balance before retrying. See When to retry.

Do failed API requests cost credits?

Error responses (401, 402, 403, 409, 410, 413, 422, 429, 451, 500, 502, 503, 504) cost 0 credits. 404 is the only exception: some endpoints charge credits when the lookup was performed but no result was found. See API pricing.

Can an aborted request be billed?

Yes. Closing the connection does not cancel the request, so it can still be billed. Set your client timeout to at least 45 seconds on the search and count endpoints, and 60 seconds on prompt search. A 504 from us costs 0 credits.

What is the difference between INSUFFICIENT_CREDITS and NO_CREDITS?

Both codes come with a 402 status. INSUFFICIENT_CREDITS means a paid lookup costs more than your remaining balance, and error.details gives the credits required and available. NO_CREDITS blocks the free /check endpoints once fewer than 2 credits are left. Both clear when you top up by purchasing more credits or upgrading your plan.

Why does a search request return 422 instead of an empty result?

The search endpoints validate every filter before running the query, because a filter that cannot be honored would otherwise return an empty page that looks like a legitimate "no results". A rejected search returns 422, costs 0 credits, and lists every problem found in error.details.errors. See Search filter validation.

What does a 451 error mean?

A 451 means this person or company asked not to be processed under GDPR or CCPA, and the lookup is permanently blocked. Profile and company lookups answer 451 whichever input names them: profile URL, email, internal id, domain or name. Find Email and Find Phone answer 451 when given the profile URL or internal id. Like the other error responses, a 451 costs 0 credits.