Verify an email address
Check whether an email address is deliverable before you send to it, read the status looot normalizes across providers, and know what a catch-all or risky result means.
people.email.verify checks one address at a time. Use it on addresses you already have, or after
people.email.find on an address you just found (see Enrich a lead list).
What it costs
Read the live price with search before you run a batch:
search {"filters": {"capability": "people.email.verify"}, "prefer": "cheapest"}
On 2026-09-28 the cheapest listed people.email.verify provider priced around $0.0019 per call.
Multiply by the number of addresses and check balance first for a batch.
Run the check
Send email. Add fallback so a provider that cannot reach the mailbox does not end the check.
run {"endpointId": "job:people.email.verify", "input": {"email": "jane.doe@example.com"}, "idempotencyKey": "verify-jane-doe-1", "fallback": true}curl -s "https://api.looot.ai/v1/runs?wait=20" \
-H "Authorization: Bearer $LOOOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endpointId": "job:people.email.verify",
"input": {"email": "jane.doe@example.com"},
"idempotencyKey": "verify-jane-doe-1",
"fallback": true
}'looot run job:people.email.verify \
--input '{"email": "jane.doe@example.com"}' \
--waitRead the status
normalized.fields.status is one of five values:
| Status | What it means |
|---|---|
valid |
the mailbox exists and accepts mail |
invalid |
the mailbox does not exist or the domain rejects it |
catch_all |
the domain accepts any address at it, so this one is unproven either way |
risky |
the provider found signals against sending (a role address, a disposable domain, a full mailbox) |
unknown |
the provider could not reach a verdict, or answered with something looot does not recognize |
normalized.fields.deliverable is a plain true/false, present only when the provider itself
reports deliverability as a separate field.
Decide what to do with each status
valid: safe to send to.invalid: drop it.catch_allandrisky: do not send automatically. Show these to a person, or run a second provider withfallbackand compare.unknown: treat likecatch_all, and note which provider answered (servedProviderId) in case a different one does better on that domain.
What the answer looks like
| Field | What it is |
|---|---|
status |
run status: completed, failed, queued, running |
outcome |
hit, weak, miss, error, rejected, skipped or pending |
servedProviderId |
the provider that actually answered |
actualCost |
what this run charged |
normalized.fields |
email, status, deliverable |
route.summary |
fallback runs only, e.g. “zerobounce: guessed ($0.01). hunter: found ($0.0245). Charged $0.0345.” |
On a miss or error
- The provider cannot reach a verdict:
statusisunknown,outcomeis oftenmiss. Withfallback, the next provider gets a try inside the same hold. - A malformed address never reaches a provider: the run fails with
validation_errorand charges nothing. The error names the field, e.g. “email: not-an-email is not an email address”. insufficient_balance: the run is blocked and not charged. Usetop_up, then retry with a newidempotencyKey.