Skip to main content

Format

All HTTP errors from the API use this envelope:

What message says

This section covers Services: a direct call on POST /services/{serviceName}, jobs[i].error in a batch, cells[key].error on a table row (/v1/tables, /v1/runs), and failed cells on a share link. Go is not covered yet. message is human text. Its words can change at any time. On a service call or a job line, do not parse it: branch on code. A table cell has no code. In cells[key].error you can rely on two things:
  • An http_NNN status, when the provider sent one. For example http_502: A data provider returned an error. Try again later.
  • The lead-in of a contact lookup. No answer yet: means the lookup has no verdict yet: run it again later. Lookup failed means the input was refused: fix it before you run it again.
message keeps the words of the error, so you can act on them: the field to fix, the value that was refused, the balance, the error of your own code in a code column, and the short reason a provider gave in words. For example http_400: invalid linkedin url. On provider_rejected, the reason comes in a sentence: http_400: A data provider refused this input: invalid linkedin url. Check the input values. These parts are removed:
  • A provider’s raw answer: an HTML page, or a JSON body (its short reason stays when it has one). A plain answer after a status is cut at 200 characters.
  • Keys, tokens and credentials (Bearer ..., Basic ..., api_key=..., password: ..., sk-..., credentials in a URL), long unbroken ids, and our internal addresses (database URLs, internal hosts, private IP addresses). Each one reads [redacted].
  • A stack trace, from its first frame.
  • A provider’s exact API host, and its pool or key ids (for example parallel-key-7). Each one reads provider.
When no words are left, or when the failure is our database’s own error, message is a fixed sentence led by the status, for example http_502: The service could not complete. Try again later. capacity and provider_unavailable use a fixed sentence, except contact lookups, which keep their lead-in.

Validation errors

On /v1/runs, /v1/tables, /v1/workspaces, and /v1/exclusions, an invalid body or query returns 400 invalid_request. message names each faulty field. details gives the same list in structured form:
  • An unknown field has an empty path and the message Unrecognized key: "…".
  • Unreadable JSON returns Request body must be valid JSON.
On /v1/go/* and /services, an invalid body returns 400 validation_error with details. A field a service does not define, at any depth, is refused and named in message: it is never dropped silently, so a misspelled criterion cannot widen a search. On POST /v1/services/jobs/batch, one invalid input refuses the whole list with 400 invalid_request, and message names the input’s index. An input the schema accepts but that names too little to search (a contact with no LinkedIn profile, and no first and last name with a company) is not refused: that line alone ends provider_rejected.

Statuses and codes

Changed on 2026-10-02

These failures were sent as 502 capability_error, and as a job line capability_error with retryable: true. They are now sent as 400 validation_error or 404 not_found, with retryable: false: sending the same input again gets the same answer. A line of a batch that ends failed is never a found: false. Its failed attempt costs nothing, except the Deep agent’s miss price when a contact lookup’s Deep agent finished its search. A call cut by a timeout can still be billed if the provider’s answer lands just after the cut. A line that ends worker_lost can also be billed: its call may have run, and been paid, before the worker was lost. The line’s credits.charged gives what it really cost, earlier attempts included. It can grow after the line ends when a charge is recorded late, most often for a call cut by a timeout whose answer landed just after the cut. Late charges usually land within seconds, but this is not guaranteed: read it again a few minutes after the end. This table is the reference for every service surface. error.retryable says whether the same input can get an answer later. “Send it again” always means a new line: a failed line is never handed out as an answer, and the new line is billed like any lookup. A found: false means every planned step finished, the Deep agent and the email verification included, and none gave an answer. One exception: a getPhoneNumber backup database (fiber or lusha, wherever it runs; never fullenrich) that could not answer (down, out of credits, rate limited, timed out, refused the call, not set up, or no time left in the lookup) is skipped, its attempt in waterfall reads category: "skipped", and the lookup still ends found: false without charge for the databases. That answer does not settle the skipped database: the same person sent again runs it again, unless databases leaves it out. getEmail’s hunter skipped ahead of fullenrich runs again after fullenrich when fullenrich did not find the email, and the line fails as unresolved if it fails again (a hunter that refuses this person counts as done). When none of the databases in databases could look (each skipped, or none can read this person, such as fiber without a LinkedIn profile or hunter without a name and company domain), the line fails instead with strategy: providers: provider_rejected when none can read this person, else provider_unavailable (a fiber or lusha that refused this person reads provider_unavailable too: with that database alone, choose another). When the Deep agent is in the strategy, its search stands: found: false at its miss price. With verifyEmail: true, a candidate the verification rejected also ends found: false: email is null, the rejected address is not returned, and verification says why. A lookup where one of those steps broke ends failed as unresolved instead.

Job failure vs HTTP error

A failed run or Go job remains a readable resource with 200. Read status and error:
Possible run.error.code values: agent_execution_failed, run_timeout (run exceeded maximum duration, 3 hours by default, counted from its start: time spent queued does not count), run_lease_lost, run_access_revoked, agent_ followed by a status. For internal codes, message may match code. For which errors to retry, see Make your integrations robust.