Enrich a lead list
Find and verify a work email for every person on a list, one row at a time, with a quote before you spend and a provider name on every result.
This walks through people.email.find and people.email.verify on one lead, then how to repeat
it for a list. It is the same loop as the find-email skill and the recipes plugin skill.
What it costs
people.email.find and people.email.verify each price per call, and the useful number for a
batch is costPerSuccessUsd: the price divided by the provider’s success rate, so a cheap provider
that mostly misses can cost more per hit than a pricier one that mostly finds. Read it from
search before you run anything:
search {"filters": {"capability": "people.email.find"}, "prefer": "cheapest"}
On 2026-09-28 the cheapest people.email.find provider listed costPerSuccessUsd around
$0.0036, and the cheapest people.email.verify provider around $0.0019 per call. Prices change, so
read the live number before quoting a customer or your own budget. Multiply by the row count for
both jobs, add them, and check balance before you start.
Find the email
Send first_name and last_name (or name) plus domain. A linkedin_url or company also
works. Give the run its own idempotencyKey, and add fallback so a miss on one provider tries
the next inside the same hold.
run {"endpointId": "job:people.email.find", "input": {"first_name": "Jane", "last_name": "Doe", "domain": "example.com"}, "idempotencyKey": "list1-row1-find", "fallback": {"maxAttempts": 3, "maxCostUsd": 0.1}}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.find",
"input": {"first_name": "Jane", "last_name": "Doe", "domain": "example.com"},
"idempotencyKey": "list1-row1-find",
"fallback": {"maxAttempts": 3, "maxCostUsd": 0.1}
}'looot run job:people.email.find \
--input '{"first_name": "Jane", "last_name": "Doe", "domain": "example.com"}' \
--waitRead normalized.fields.email when it is present, or result otherwise. An outcome of weak
with normalized.fields.check (or verdict) equal to guessed is a pattern guess, not a
confirmed address: verify it before you keep it, same as a found one.
Verify the email
Run people.email.verify on every address you plan to keep, with a fresh idempotencyKey:
run {"endpointId": "job:people.email.verify", "input": {"email": "jane.doe@example.com"}, "idempotencyKey": "list1-row1-verify", "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": "list1-row1-verify",
"fallback": true
}'looot run job:people.email.verify \
--input '{"email": "jane.doe@example.com"}' \
--waitRead the verdict and keep only valid
normalized.fields.status (email verify) is one of valid, invalid, catch_all, risky or
unknown. Keep valid. Flag catch_all and risky for the person reviewing the list; don’t send to
them outright, and drop invalid and unknown.
Repeat, a few rows at a time
Run a handful of leads at once, not the whole list in parallel. On too_many_inflight_runs,
wait a couple of seconds and retry with the same idempotencyKey. When the list is done, report
how many were found, how many verified valid, and the total spent (sum of actualCost).
What the answer looks like
A completed run carries:
| Field | What it is |
|---|---|
status |
completed, failed, queued or running |
outcome |
hit, weak, miss, error, rejected, skipped or pending |
servedProviderId |
which provider actually answered (can differ from the one you asked for, after a fallback) |
actualCost |
what this run charged, in USD |
normalized.fields |
on people.email.find: email, check; on people.email.verify: email, status, deliverable |
route.summary |
fallback runs only, one line per attempt and the total charged |
On a miss or error
- No address found:
outcomeismiss. Withfallback, the run already tried other providers inside the hold before giving up. - Only a guessed pattern:
outcomeisweak,check/verdictisguessed. Verify it before you use it; a wrong guess fails verification and costs nothing extra beyond the verify call. - Verify comes back
catch_allorrisky: the mailbox may or may not exist. Show it to a person before anything is sent. too_many_inflight_runs: wait a couple of seconds and retry the sameidempotencyKey.insufficient_balance: the run is blocked, not charged. ShowtopUp.checkoutUrlfrom the error, or calltop_up, then retry with a newidempotencyKey.