Find business addresses for several people
The same lookup for several people at one domain. Needs the Starter plan or higher, like POST /v1/find: on the Free plan it is refused with 403 PLAN_UPGRADE_REQUIRED before anything is charged. Retry-safe for FIFTEEN MINUTES: the receipt a completed lookup writes has a 15-minute TTL, so a repeat inside that window replays the summary (rather than the individual results) and charges nothing, and a repeat after it is a new paid lookup. A delayed retry is not free.
What it charges, before it starts. 5 credits per person (the single finder's price) multiplied by the number of people whose names are usable, debited up front. Up to 50 people per request, so up to 250 credits on one call.
"Usable" is the same rule as POST /v1/find: both names must contain at least one letter after normalization (spelled in a-z: accents dropped, ø as o, ß as ss and so on, then only a-z kept), and no letter without a single a-z spelling. A person who fails it comes back as an invalid_name row, with a message saying why, and is not charged for, so the debit is 5 x the chargeable count rather than 5 x the list length. Count the usable names yourself before submitting if the difference matters: nothing in the request tells you which rows will be free.
A special-use domain is a 400 before the charge. .example, .test, .invalid, .localhost, .local, .onion, .alt, .internal, .corp, .home, .mail, and home.arpa with the other special-use .arpa names are never answered for in the public DNS, so no address under them can exist and every candidate could only fail. So are RFC 2606's documentation domains example.com, example.net and example.org and any name under them: they resolve, but publish a null MX, and the worker refuses them before SMTP. They used to be searched and billed (5 credits per usable name) for a not_found that was certain before the search began. A name over 64 characters, or one that spells to more than 64 letters, is a 400 for the whole request.
A domain that cannot receive mail, or a public mailbox provider, is a 400 for the whole request before the charge, exactly as on POST /v1/find, and a failed mail-server lookup is an uncharged 503. An earlier attempt of the same request that was already charged, replayed or recorded is still answered from its retry protection instead.
A person whose probes settled nothing is not_checked and refunded: a probe that is unknown, a policy block, greylisting, a temporary failure or risky without evidence that the mailbox exists is never that person's address.
Only a row with verified: true is a confirmed address, exactly as on POST /v1/find. A row with an email and verified: false is a best guess, and found counts it; verified_found counts only the confirmed rows.
Request
- Base URL: https://api.kawaa.com
- URL: https://api.kawaa.com/v1/find/bulk
- Auth: one of:
- API key in header X-Api-Key
- HTTP bearer
- OAuth 2 (authorization code, authorization URL https://api.kawaa.com/oauth/authorize, token URL https://api.kawaa.com/oauth/token, scopes: verification:write)
Headers
Makes a charging call safe to retry: a repeat under the same key replays the original outcome instead of buying the work again. The window depends on the route: the receipt behind it expires, and after that the same key is a new paid request. It is 30 days for a deliverability check, at least 30 days for a single find, 24 hours for an activity lookup, and 15 minutes for a bulk find. What a replay returns also differs: a batch replays its JOB, an activity lookup replays its RESULT. A different request under the same key is 409 IDEMPOTENCY_KEY_CONFLICT, before any charge.
How long a key stays safe differs per route, and for the verification routes it depends on the plan. POST /v1/verify, /v1/verify/batch and /v1/verify/file keep their receipts for the job's data-retention period, never less than 30 days, so longer on plans that keep results longer. The job itself is kept only for the plan's data-retention period, which is 7 days on Free: a repeat that arrives after the job's retention period has ended but while its receipt is still live is refused with 410 IDEMPOTENT_JOB_EXPIRED and charged nothing, rather than bought a second time; send a new key to run it again. Once the receipt expires the same key claims a fresh target and creates another paid job. POST /v1/deliverability/check keeps its receipt 30 days. POST /v1/find keeps its receipt at least 30 days and its stored address only for the plan's retention period; a repeat after the address expires but before the receipt does return 410 IDEMPOTENT_JOB_EXPIRED without charging. The activity lookups keep receipts about 24 hours, and POST /v1/find/bulk 15 minutes. Without a key, GET /v1/activity/{email} and GET /v1/activity/domain/{domain} still answer an identical repeat from the same account free for 15 minutes after the lookup that was charged (POST /v1/find/bulk does the same for its content); a key buys a fresh read inside that window. An unkeyed POST /v1/verify that arrives while an identical one from the same account is still being verified (at most 15 minutes after its charge) joins it and is not charged; once that verification has its answer, a repeat is charged as usual. Only a key makes a later retry free.
Retry with the key you have rather than minting a new one. A key is not a refund (it prevents a second charge, it does not undo the first), but it stays good far longer than the shortest advertised retention suggests, and dropping one early is how a retry becomes a second paid job.
The characters are checked after trimming. Surrounding whitespace is trimmed from the header first; the resulting key must be 1-255 printable ASCII characters (0x21-0x7e) other than a comma, with no internal spaces or anything outside that range. Leading and trailing whitespace is accepted and is not part of the key. Because OpenAPI constraints apply to the raw header rather than the post-trim value, this schema deliberately has no minLength, maxLength, or pattern; those constraints would make generated clients reject values the API accepts. Invalid post-trim values get 400 before the route starts. A UUID is the obvious choice. Sending the header twice with different raw values is also a 400. A key may not contain a comma, because a header sent more than once reaches the API joined by commas; a comma is refused with the same 400, before anything is charged. A receipt claimed under a comma-bearing key before 2026-10-03 still replays for its window.
Request body
Response
The searches that were run. Not necessarily one result per person: when the request approaches its time limit the API stops, refunds the chargeable tail it did not process, and still answers 200 with processed below total and a shorter results array. Compare processed with total before treating a missing person as not found, and read credits_used for what was actually charged after the refund.
Changes
No recorded changes to this operation across all 1 revision of this API.