Skip to content
LeadityDocs

Using the API

Errors

Every refusal answers the same small body with a stable code. This is the whole list.

A refusal answers an HTTP status and a JSON body with two fields:

{
  "error": "quote_exceeded",
  "message": "The price is above the maxCredits the call allowed. Nothing was charged."
}

error is a stable code: branch on it. message is a plain sentence for a person and may be reworded. Some codes carry extra fields, listed beside each code in the table below, such as retryAfterSeconds on rate_limited and issues on invalid_body. A field is present when that refusal has it to say. A field you do not recognise can be ignored.

A refusal the HTTP API makes itself, before the call reaches Leadity's servers, also carries docs: a link to its code's row on this page.

Issues

A request that does not match its operation's schema is refused with invalid_body, or with its field's own code such as invalid_country, and issues lists what is wrong. Each entry has a path and a message. The path is the field as a dotted path, led by the part of the request it travels in: path, query or body.

{
  "error": "invalid_body",
  "message": "The request does not match the operation's schema.",
  "issues": [{ "path": "body.items.1", "message": "an item holds one value: no line break, comma, semicolon or tab" }]
}

body.items.1 is the second entry of the body's items, query.state is the state query parameter, and path.id is the id in the URL. The path is spelled the same way whichever check refused the call. On the MCP server a tool takes one input object, and the path still names the part of the operation the argument belongs to: body.items is the tool's items argument.

On the MCP server the same body comes back as the tool call's error result, with the HTTP status beside it as httpStatus. A field named status is always the code's own, such as the final status of a run on not_running.

Every code

CodeStatusMeaningExtra fields
unauthorized401The API key is missing, malformed, revoked or expired.-
wrong_key_kind401This key opens the other surface: an API key works on the HTTP API, an MCP key on the MCP server.-
insufficient_scope403The key does not carry the scope this operation needs.requiredScope: The scope the operation needs: read, write or spend.
account_suspended403The account is suspended.-
email_not_verified403The account's email address is not verified yet.-
paywall_required402This operation needs a purchase on the account first.-
package_locked402Until its first purchase the account may order only the starter packages. The body names what was ordered and what is allowed.ordered: The package the order's filters read as, custom for any other set.allowed: The packages the account may order now.
tenant_not_found404The account no longer exists.-
invalid_body400The request does not match the operation's schema.issues: The problems found, each with path (the field as a dotted path, led by the part of the request it travels in: path, query or body, such as body.items.1) and message (what is wrong there).
invalid_json400The request body is not valid JSON.-
payload_too_large413The request body is too large.-
not_found404No such resource on this account.id: On export_scrapes: the first id that is not one of the account's scrapes.
idempotency_key_required400A spending operation must carry an Idempotency-Key header (16 to 64 characters of A-Z, a-z, 0-9, _ and -).-
invalid_idempotency_key400The Idempotency-Key must be 16 to 64 characters of A-Z, a-z, 0-9, _ and -.-
idempotency_conflict409This Idempotency-Key was already used for a different request.-
max_credits_required400A spending operation must carry maxCredits, the most it may take.-
quote_exceeded409The price is above the maxCredits the call allowed. Nothing was charged.credits: What the call would have taken: the whole hold on an order or a list, the price or the hold on an upgrade.maxCredits: The ceiling the call sent.reviewsReservedCredits: On create_scrape: the part of credits held for review scraping.count: On purchase_phone_reveal, when the count was the cause: how many numbers the reveal would name.maxNumbers: On purchase_phone_reveal, when the count was the cause: the maxNumbers the call sent.
price_changed409The price moved above the ceiling the call allowed. Nothing was charged; quote again.-
insufficient_credits402The balance does not cover the price. Nothing was charged.required: On create_validation: the credits the list holds.
invalid_country400The country is not one Leadity holds territory for.issues: The problems found, each with path (the field as a dotted path, led by the part of the request it travels in: path, query or body, such as body.items.1) and message (what is wrong there).
invalid_state400The region is missing.issues: The problems found, each with path (the field as a dotted path, led by the part of the request it travels in: path, query or body, such as body.items.1) and message (what is wrong there).
invalid_query400The search text is missing.issues: The problems found, each with path (the field as a dotted path, led by the part of the request it travels in: path, query or body, such as body.items.1) and message (what is wrong there).
invalid_niche400No niche id was given.issues: The problems found, each with path (the field as a dotted path, led by the part of the request it travels in: path, query or body, such as body.items.1) and message (what is wrong there).
too_many_niches400Too many niches in one call. The body carries max.max: The most niches one call may name.
unknown_niche400A niche id is not in the catalog. The body names it.niches: The ids the catalog does not hold.findOwner: On an order or an estimate: whether it asked for the owner lookup.
niche_unavailable400A niche cannot be ordered with these filters, usually the owner lookup. The body names it.niches: The ids that cannot be ordered so.findOwner: On an order or an estimate: whether it asked for the owner lookup.
no_areas_selected400The selection names no area.-
selection_too_large400The selection names more areas than one order may. The body carries max.max: The most areas one order may name.
below_minimum400The quantity is under the smallest one this operation takes: targetLeads (the body carries min), or an upgrade's target (list_scrape_offers names it).min: On create_scrape: the smallest targetLeads the account may order.
below_minimum_credits400The leads cost less than the smallest order. The body carries min, credits and minLeads, the smallest lead count that clears it.min: The smallest order, in credits.credits: What the leads ordered come to, in credits.minLeads: The smallest targetLeads that clears both floors at this order's rate.
below_minimum_work409Too little of the selection is still unswept to run a scrape. The body carries min and workItems.min: The fewest searches a scrape runs.workItems: The searches this order would run, when it was counted.
too_much_work400The order would run more searches than one order may. The body carries max.max: The most searches one order may run.workItems: The searches this order would run, when it was counted.
all_covered409Every area selected is already swept for these niches. Choose other areas.-
all_in_progress409Every area selected is held by another live scrape of this account. Try again once it ends.-
too_many_active409The account has as many live runs as it may. The body carries max.max: The most live runs of this kind the account may hold.
create_busy409Another order of this account is being placed right now. Retry in a moment.-
seed_failed503The scrape could not be planned. Nothing was charged; retry with the same Idempotency-Key.-
not_active409The scrape is no longer live, so there is nothing to stop.-
not_running409The validation run is already final. The body carries its status.status: The run's final status.
no_offer409The scrape has nothing of that kind to sell, or it is still running. Read list_scrape_offers.-
already_purchased409This upgrade was already bought on this scrape.-
already_running409An owner-name run is already going on this scrape.-
not_eligible409This scrape cannot take an owner-name run: it ordered the owner lookup, or it is still running.-
no_pool409No delivered lead is left for an owner-name run to try.-
empty_list400No item in the list is a phone number or an address. The body carries submitted.submitted: The lines or items submitted.
list_too_large413The list is larger than one run takes: over 8 MB of text. The body carries maxItems.maxItems: The most items one run checks.
numbering_unavailable409The list holds numbers whose country register is not loaded yet. The body names the plan.region: The region the list was read under.plan: The register not loaded yet: gb or au.
unknown_pack400No plan has that sku. list_plans names them.-
subscription_exists409A monthly plan still renews. A person cancels it on Whop before another size can start.-
same_plan_live409A cancelled plan of this size is still running out. The body carries endsAt, when it ends.endsAt: When the last of that plan's credits expire, or null when that is not still ahead.
invalid_amount400The credits are not an amount a top-up takes. The body carries min, max and step.min: On create_top_up_checkout: the smallest top-up, in credits.max: On create_top_up_checkout: the largest top-up one purchase may be, in credits.step: Every top-up is a whole multiple of this many credits.
no_subscription409A top-up adds credits to a monthly plan, and the account has no live plan. create_checkout sells a one-time pack instead.-
unpriceable_plan409The account's plan carries no price a top-up can be rated at. Contact Leadity support.-
checkout_failed502Whop did not open the checkout. Try again.-
rate_limited429Too many requests. Wait for the Retry-After seconds and try again.retryAfterSeconds: The seconds to wait, the same number as the Retry-After header.
try_again503Leadity could not answer just now. Retry in a moment, with the same Idempotency-Key on a spending call.retryAfterSeconds: When a request with this Idempotency-Key is still being processed: the seconds to wait before retrying with the same key, as the Retry-After header says.
internal_error500Something failed on our side. Nothing was charged twice.body: On the MCP server, when Leadity answered in a shape the tool does not publish: that answer as it came.

An operation's reference page lists the codes every call of its kind can meet: the key and account checks, a malformed request, the limits and, on a spend, the money rules. The codes an order or an upgrade adds of its own, such as an order below the account's minimum or an area already swept, are in the table above, and the operation's description says what to change.

Handling errors

  • 4xx except 429: the request is wrong as sent. Read error, fix the request, and only then try again.
  • 409 idempotency_conflict: the key already named a different request. Do not retry it: send the new request under a new key.
  • 429 rate_limited: wait the number of seconds in the Retry-After header, then retry the same request.
  • 503 try_again and 500 internal_error: retry with backoff. A try_again from the HTTP API always carries Retry-After: wait that many seconds first. On a spending call, retry with the same Idempotency-Key: if the first attempt went through, the retry replays its answer instead of charging again. A read has no key to keep, so it simply retries. See Idempotency.
Node.js
const response = await fetch(url, init);
if (!response.ok) {
  const { error, message } = await response.json();
  if (error === "rate_limited") {
    const wait = Number(response.headers.get("retry-after") ?? 30);
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  } else {
    throw new Error(`${error}: ${message}`);
  }
}