LeadlistVerifier

Developer docs

Email verification API reference.

Two-pass email verification over a REST API. Bearer auth, predictable JSON, bulk endpoints up to 10,000 addresses per call, and HMAC-signed webhooks. Generate a key in the dashboard to get started.

Quickstart

Three steps to your first verification.

  1. 1. Generate an API key in your dashboard. Copy the full key when shown. You will not see it again.
  2. 2. Make a verification request:
curl https://www.leadlistverifier.com/api/v1/verify?email=jeremy@elevateclientsinc.com \
  -H "Authorization: Bearer llv_live_..."

3. Read the JSON response. result is deliverable, undeliverable, risky, or unknown. One credit is deducted on success.

Authentication

All requests require an API key passed in the Authorization header as a bearer token. Keys are stored as SHA-256 hashes; we cannot recover a lost key. Manage keys in the dashboard.

Authorization: Bearer llv_live_<32 hex chars>

Revoked or missing keys return a 401 with the standardized error shape (see Error codes).

Base URL

https://www.leadlistverifier.com/api/v1

Rate limits

Every API key is limited to 60 requests per minute. These headers are returned on every response (success and error):

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1745510400

When you exceed the limit, the response is 429 rate_limit_exceeded with a Retry-After header indicating how long to wait (seconds).

Single verification

GET/api/v1/verify
POST/api/v1/verify

Run the two-pass engine on a single address. Deducts 1 credit on success. If the verification engine fails, the credit is refunded automatically.

Parameters

ParameterTypeRequiredDescription
emailstringYesAddress to verify. Use ?email= for GET or { email } in the JSON body for POST.

Examples

curl
curl "https://www.leadlistverifier.com/api/v1/verify?email=alex@orbital.tech" \
  -H "Authorization: Bearer $LLV_API_KEY"
Python
import os, requests

res = requests.get(
    "https://www.leadlistverifier.com/api/v1/verify",
    params={"email": "alex@orbital.tech"},
    headers={"Authorization": f"Bearer {os.environ['LLV_API_KEY']}"},
)
res.raise_for_status()
print(res.json())
Node
const res = await fetch(
  "https://www.leadlistverifier.com/api/v1/verify?email=alex@orbital.tech",
  { headers: { Authorization: `Bearer ${process.env.LLV_API_KEY}` } }
);
if (!res.ok) throw new Error(`Status ${res.status}`);
const body = await res.json();
console.log(body);

Success response (200)

{
  "email": "alex@orbital.tech",
  "result": "deliverable",
  "score": 91,
  "engine": "two_pass",
  "is_role": false,
  "is_free": false,
  "is_disposable": false,
  "is_catch_all": true,
  "did_pass_two": true,
  "credits_remaining": 4932
}

Result values

ResultDescription
deliverableEmail exists and accepts mail.
undeliverableEmail does not exist or the domain is invalid.
riskyCatch-all domain or low-confidence verification.
unknownCould not be determined by the two-pass engine.

Engine

primary indicates the result was returned by the primary SMTP verification engine alone. two_pass means the proprietary catch-all resolution engine ran a second pass to produce a definitive verdict on a catch-all or unknown address.

Bulk verification

POST/api/v1/verify-bulk

Submit up to 10,000 addresses per call. Credits are deducted up front; the engine runs asynchronously and you receive a job_id to poll for status or to subscribe to via the bulk.completed webhook.

Request body

ParameterTypeRequiredDescription
emailsstring[]NoArray of addresses (max 10,000). Required unless list_id is provided.
list_idstringNoRe-run verification on an existing list. Mutually exclusive with emails.

Examples

curl
curl -X POST https://www.leadlistverifier.com/api/v1/verify-bulk \
  -H "Authorization: Bearer $LLV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails": ["one@example.com", "two@example.com"]}'
Python
import os, requests

res = requests.post(
    "https://www.leadlistverifier.com/api/v1/verify-bulk",
    headers={"Authorization": f"Bearer {os.environ['LLV_API_KEY']}"},
    json={"emails": ["one@example.com", "two@example.com"]},
)
res.raise_for_status()
job = res.json()
print("job_id:", job["job_id"])
Node
const res = await fetch(
  "https://www.leadlistverifier.com/api/v1/verify-bulk",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LLV_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ emails: ["one@example.com", "two@example.com"] }),
  }
);
const job = await res.json();
console.log("job_id:", job.job_id);

Success response (200)

{
  "job_id": "clxyz123abc",
  "status": "queued",
  "count": 2,
  "credits_used": 2,
  "credits_remaining": 4930,
  "webhook_url": "https://your-app.example.com/webhooks/llv"
}

Job status

GET/api/v1/verify-bulk/:job_id

Poll for the status of a bulk job. When status is completed, results_url is a CSV export endpoint you can fetch.

Example

curl https://www.leadlistverifier.com/api/v1/verify-bulk/clxyz123abc \
  -H "Authorization: Bearer $LLV_API_KEY"

Response shape

{
  "job_id": "clxyz123abc",
  "status": "completed",
  "total": 10000,
  "completed": 10000,
  "deliverable": 7800,
  "undeliverable": 1200,
  "risky": 800,
  "unknown": 200,
  "results_url": "https://www.leadlistverifier.com/api/v1/lists/clxyz123abc/export?filter=all",
  "webhook_delivered": true,
  "created_at": "2026-05-13T14:22:01.000Z",
  "completed_at": "2026-05-13T14:38:44.000Z"
}

status is one of queued, processing, completed, or failed.

List management

Inspect and export lists created by previous bulk jobs or via the web dashboard.

List index

GET/api/v1/lists

Cursor-paginated. Up to 200 lists per page. ?limit= (1-200, default 50), ?cursor= for the next page.

curl "https://www.leadlistverifier.com/api/v1/lists?limit=20" \
  -H "Authorization: Bearer $LLV_API_KEY"

List detail

GET/api/v1/lists/:id
{
  "id": "clxyz123abc",
  "file_name": "api-bulk-1747000000.csv",
  "status": "completed",
  "raw_row_count": 10000,
  "total": 9842,
  "completed": 9842,
  "duplicates_removed": 158,
  "credits_charged": 9842,
  "deliverable": 7680,
  "undeliverable": 1180,
  "risky": 790,
  "unknown": 192,
  "created_at": "2026-05-13T14:22:01.000Z",
  "completed_at": "2026-05-13T14:38:44.000Z",
  "error_message": null
}

Per-result paging

GET/api/v1/lists/:id/results

Cursor-paginated, up to 1,000 results per page. Filter with ?status=deliverable|undeliverable|risky|unknown.

CSV export

GET/api/v1/lists/:id/export

Returns CSV. Required: ?filter=all|deliverable|undeliverable|risky. List must be in completed status.

curl "https://www.leadlistverifier.com/api/v1/lists/clxyz123abc/export?filter=deliverable" \
  -H "Authorization: Bearer $LLV_API_KEY" \
  -o deliverable.csv

Credits balance

GET/api/v1/credits

Returns the caller’s current credit balance and total credits consumed in the last 30 days.

curl https://www.leadlistverifier.com/api/v1/credits \
  -H "Authorization: Bearer $LLV_API_KEY"

Response

{
  "credits_remaining": 4932,
  "credits_used_30d": 1068
}

Webhooks

Configure a webhook in the dashboard to receive HMAC-SHA256 signed event notifications. One webhook URL per account. Up to 5 retries with exponential backoff on non-2xx responses.

Event types

EventWhen it fires
verification.completedFires after every successful single verification via /api/v1/verify.
bulk.completedFires when a bulk job finishes. Payload includes final counts and a results_url for CSV export.
bulk.failedFires when a bulk job fails. Credits are refunded automatically before this event is delivered.

Delivery headers

Content-Type: application/json
User-Agent: LeadListVerifier-Webhook/1
X-LLV-Event: bulk.completed
X-LLV-Timestamp: 1745510400
X-LLV-Signature: sha256=8f4e2c...

Payload (bulk.completed)

{
  "event": "bulk.completed",
  "timestamp": "2026-05-13T14:38:44.000Z",
  "data": {
    "job_id": "clxyz123abc",
    "list_id": "clxyz123abc",
    "total": 10000,
    "deliverable": 7800,
    "undeliverable": 1200,
    "risky": 800,
    "unknown": 200,
    "results_url": "https://www.leadlistverifier.com/api/v1/lists/clxyz123abc/export?filter=all"
  }
}

Verifying signatures

Compute HMAC-SHA256 of the raw request body using your webhook signing secret, then compare to the value after sha256= in X-LLV-Signature. Use a timing-safe comparison.

Python
import hmac, hashlib

def verify(secret: str, body: bytes, sig_header: str) -> bool:
    received = sig_header.removeprefix("sha256=")
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(received, expected)
Node
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, body, sigHeader) {
  const received = sigHeader.startsWith("sha256=")
    ? sigHeader.slice(7)
    : sigHeader;
  const expected = createHmac("sha256", secret).update(body).digest("hex");
  const a = Buffer.from(received, "hex");
  const b = Buffer.from(expected, "hex");
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

Sign the raw request body bytes. Re-serializing the JSON before signing will break the comparison.

Error codes

All errors return a JSON body with this shape:

{
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough credits to verify this address."
  }
}
StatusCodeMeaning
400invalid_requestRequest body or query parameters are missing or malformed.
401unauthorizedMissing, invalid, or revoked API key.
402insufficient_creditsNot enough credits to perform the requested operation.
403forbiddenAuthenticated key is not allowed to access this resource.
404not_foundResource does not exist or does not belong to this account.
429rate_limit_exceededExceeded 60 requests per minute. Wait until Retry-After.
500internal_server_errorUnexpected server error. Safe to retry with backoff.