Errors

One envelope, a fixed vocabulary, and one promise about billing.

On this page

Every failure has the same shape, whatever went wrong and however far into the stack it got.

JSON
{
  "error": {
    "type": "unprocessable_target",
    "message": "Profile is private, deleted or unreachable.",
    "requestId": "req_8f2ka91x"
  }
}

Switch on type, never on message. The type is the contract and will not be repurposed or re-cased once shipped; the wording exists for a human reading a log and can improve.

requestId is on every response, success or failure. It is the one thing a support conversation can start from, so keep it in your logs.

The vocabulary #

StatustypeMeans
400invalid_requestThe body or a parameter could not be accepted. error.field names it where there is one.
401invalid_api_keyMissing, malformed, revoked or expired key.
402insufficient_creditsThe prepaid credit balance cannot cover the submission. Its own status because its own remedy: quota_exhausted clears when the month turns and this one clears when credits are bought, so a client retrying on a schedule would retry this forever. Top up and the same request runs. quota.balance on GET /account is the number to watch.
403quota_exhaustedThis month's allowance cannot cover the submission.
403insufficient_scopeThe key is valid and is the wrong one: it does not carry the scope this route needs. The message names the scope. Never retry it - mint a key that carries it. See Authentication.
403email_unverifiedNobody on the account has confirmed their email address, and the account is on the default allowance rather than an agreed one. Confirm it and the same request works; waiting for the month to turn does nothing.
404not_foundNo such batch, or it belongs to another organization. The two are deliberately indistinguishable.
409conflictThe batch has already finished.
422unprocessable_targetWell-formed, but the target could not be acted on.
422capacity_unavailableWe had no capacity able to run the item right now. Every operation can answer it; the message narrows it, because a Sales Navigator search needs capacity of a kind the others do not and can be short while everything else is fine. You get this rather than a thinner result, and credits_used is 0 - retrying later costs you only the wait.
429rate_limitedToo many requests, or too many batches in flight. Carries Retry-After.
500internal_errorOurs. The requestId is how we find it.
501not_implementedNo operation returns this today - every one is answerable. It stays in the list because it is still a valid error.type, and it is what a newly documented operation would answer before its scraper lands.
504upstream_timeoutLinkedIn did not answer in time.

The billing promise #

If you did not get a record, you were not charged for it. Every error above is unmetered, and a failed result entry always carries credits_used: 0 - enforced by a database constraint rather than by a code path remembering.

Per-item errors #

A result entry uses the same vocabulary, minus the types that can only apply to a whole request. One failed item never fails a batch.

JSON
{
  "item_index": 1,
  "input": { "urn": "ACoAAA8BYqEBCGLg" },
  "status": "failed",
  "credits_used": 0,
  "error": { "type": "unprocessable_target", "message": "Profile is private, deleted or unreachable." }
}

Retrying #

  • 429 and 5xx are worth retrying, with backoff. Honour Retry-After when it is there.
  • 4xx other than 429 will fail again identically. Fix the request.
  • Always retry a create with the same Idempotency-Key, so a lost response cannot become a duplicate batch.