Email Finder
Given a contact's full name and the domain of the company they work for, returns a verified email address along with verification metadata.
Under the hood, the service maintains a learning pattern store: it tries patterns previously verified for the domain first (the "warm path"), then falls back to a parallel sweep of all common templates against the Emailable verifier (the "cold path") if no cached pattern verifies. Patterns are global — every customer's verification feeds every other customer's first lookup at that domain.
Names with European tussenvoegsels (Joey van Ommen,
Joey van der Ommen) are handled — the service tries both
"tussenvoegsel-included" and "tussenvoegsel-excluded" surname
renderings.
Response semantics
Always branch on the status field first — do not null-check
verification directly.
status: found→email + verificationare present; a deliverable or risky mailbox was found at this domain.status: not_found→emailandverificationare explicitnull; we tried the full candidate set within a 15-second budget and no mailbox verified as deliverable. Distinct from a server error. A non-person input (a company name in the name field) also settles here rather than as a 422.- Non-Latin names (Arabic, Chinese, etc.) are transliterated
to ASCII and attempted on a best-effort basis — they are no
longer auto-
not_found. Because transliteration is approximate (Arabic omits short vowels), such names — and heavily-recovered long compound names — are treated as low-confidence: they returnfoundonly on a confirmed deliverable mailbox, and never return a catch-all "best guess" (they settle asnot_found/inconclusiveinstead). status: inconclusive→emailandverificationarenull, but this is not a definitive miss: the mail server greylisted us (a temporary "try again later" deferral) or the verification budget ran out before we could confirm. The contact is likely reachable — retry the same request later rather than discarding it. Previously these cases surfaced as a408timeout error; they now return a stable200so an identical request no longer flips between error and not-found.verification.accept_all = true→ the domain is configured as catch-all, so the verifier cannot distinguish real mailboxes from accepted-but-discarded ones. The returned email is our best guess (modal real-world pattern) but should be treated as lower confidence. This catch-all best guess is only returned for high-confidence parses; a low-confidence name (transliterated or heavily-recovered compound) settles asnot_foundinstead.
Credits
Each call charges 0.1 credits when it produces an answer —
both when a deliverable email is found and when the definitive
answer is not_found after real verification work. A found
result always charges, including when it is served from Saber's
verification cache: you pay per answer delivered, not per
verification run. You are NOT charged when:
- the result is
inconclusive— we couldn't determine an answer, and the advised retry of the same request would otherwise pay twice for one answer; - no verification work was needed and no email was found:
validation errors, upstream outages, pre-filter short-circuits
(e.g. invalid domain syntax), and
not_foundrepeats served entirely from Saber's verification cache; - the only verification work ran on a domain Saber discovered speculatively (dead-domain successor search) rather than the domain you supplied.
Requests rejected due to insufficient credit balance return 402
before any verification work is performed.
Authorization
ApiKeyAuth API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Full name of the contact. Accepts 2-4 token names and
European name particles (Joey van Ommen,
Joey van der Ommen). Trailing credentials and
honorifics (Ahmed Othman, MBA, CDMP, Dr. …),
parenthetical roles, and mixed-script tails are cleaned
off before parsing, so those resolve rather than being
rejected. Non-Latin names (Arabic, Chinese, etc.) are
transliterated and attempted best-effort as
low-confidence parses. A value that is actually a
company name (Best Vision Business Center) settles as
not_found (not an error). A genuinely unparseable
name — a single lone token, or empty — is rejected with
422.
1 <= length <= 200The mail domain for the contact's company. Accepts
bare domains (saber.app), domains with www.
prefix, and trailing-dot forms. URL-shaped inputs
(https://saber.app) and email-shaped inputs
(joey@saber.app) are rejected with 422.
If the domain redirects (a rebrand) or is dead (no
mail), the finder resolves the company's real domain
and searches there too — supplying company_name
and/or location makes that discovery far more
accurate.
1 <= length <= 253Optional. The contact's company name. Used only when
the supplied domain is dead or redirects: it anchors
the web search that finds the company's real domain,
and sharply reduces wrong-company matches. Strongly
recommended whenever the domain may be stale.
length <= 200Optional. The contact's location (city / region / country). Enables location-aware candidate generation for global companies — e.g. a London contact at a firm with country domains resolves to the regional domain or subdomain.
length <= 200Response Body
application/json
application/json
application/json
curl -X POST "https://example.com/v1/contacts/find-email" \ -H "Content-Type: application/json" \ -d '{ "full_name": "Joey Ommen", "domain": "saber.app" }'{ "status": "found", "email": "joey.vanommen@saber.app", "verification": { "state": "deliverable", "score": 95, "accept_all": false }}Accepts up to 1,000 (full_name, domain) pairs and resolves
them asynchronously through the same engine as the single
find-email endpoint. Returns 202 with a batch_id
immediately; poll GET /v1/contacts/find-email/batch/{id} for
progress and per-contact results.
Server-side behaviors that replace client-side loops:
- Pacing + retries against the upstream verifier — no client-side 429 handling needed; throttle waves are absorbed with backoff inside the batch.
- Domain grouping — contacts at the same domain are resolved together, so the first resolution warms the pattern store and the rest reuse it.
- Domain normalization + duplicate collapsing — domains are
normalized (lowercased,
www.and trailing dots stripped) before grouping, and case-insensitive duplicate(full_name, domain)pairs are collapsed;row_countis the deduplicated count. Poll results echo the normalized domain. - Partial results, always — an upstream failure affecting
one domain never discards other rows' results; affected rows
end in
status: errorwith anerror_code.
Authorization
ApiKeyAuth API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
1 <= items <= 1000Response Body
application/json
curl -X POST "https://example.com/v1/contacts/find-email/batch" \ -H "Content-Type: application/json" \ -d '{ "contacts": [ { "full_name": "Aadil Merchant", "domain": "collectiveartists.com" }, { "full_name": "Kathy Kaplan", "domain": "collectiveartists.com" } ] }'{ "batch_id": "4da22c97-b7d5-4e31-8c3a-03870ebc7b20", "status": "pending", "row_count": 0}Returns the batch status, progress counters, and every
contact's current outcome. Rows resolve incrementally — poll
until status is completed (or, rarely, failed).
Per-contact status values:
pending— not resolved yet.found—email+verificationpresent (same shape as the single endpoint).not_found— resolved definitively with no verified mailbox;emailandverificationare explicitnull.inconclusive— reachable but unconfirmed (greylisting / verification budget exhausted);emailandverificationarenull. Same meaning as the single endpoint — not a definitive miss, retry the contact later. Distinct fromnot_found.error— terminal failure for this row;error_codeis one ofinvalid_input(name/domain unparseable),upstream_failed(verifier failure on this domain), orincomplete(the batch finished but this row's domain group exhausted its retries).
Batches are retained for 30 days after creation.
Authorization
ApiKeyAuth API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.
In: header
Path Parameters
The batch_id returned by the submit call.
uuidResponse Body
application/json
curl -X GET "https://example.com/v1/contacts/find-email/batch/497f6eca-6276-4993-bfeb-53cbbbba6f08"{ "batch_id": "6f1a1c9e-9df9-4a4f-9df0-2f8f6f7a4b31", "status": "running", "progress": { "total": 3, "pending": 1, "found": 1, "not_found": 1, "inconclusive": 0, "error": 0 }, "contacts": [ { "full_name": "Aadil Merchant", "domain": "collectiveartists.com", "status": "found", "email": "aadil.merchant@collectiveartists.com", "verification": { "state": "deliverable", "score": 93, "accept_all": false } }, { "full_name": "Manjula K.L", "domain": "capstonelife.in", "status": "not_found", "email": null, "verification": null }, { "full_name": "Kathy Kaplan", "domain": "relevatehealth.com", "status": "pending", "email": null, "verification": null } ]}