Email Finder

POST
/v1/contacts/find-email

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: foundemail + verification are present; a deliverable or risky mailbox was found at this domain.
  • status: not_foundemail and verification are explicit null; 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 return found only on a confirmed deliverable mailbox, and never return a catch-all "best guess" (they settle as not_found/inconclusive instead).
  • status: inconclusiveemail and verification are null, 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 a 408 timeout error; they now return a stable 200 so 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 as not_found instead.

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_found repeats 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
AuthorizationBearer <token>

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*string

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.

Length1 <= length <= 200
domain*string

The 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.

Length1 <= length <= 253
company_name?string

Optional. 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.

Lengthlength <= 200
location?string

Optional. 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.

Lengthlength <= 200

Response 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  }}

POST
/v1/contacts/find-email/batch

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_count is 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: error with an error_code.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

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.

contacts*array<>
Items1 <= items <= 1000

Response 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}
GET
/v1/contacts/find-email/batch/{id}

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.
  • foundemail + verification present (same shape as the single endpoint).
  • not_found — resolved definitively with no verified mailbox; email and verification are explicit null.
  • inconclusive — reachable but unconfirmed (greylisting / verification budget exhausted); email and verification are null. Same meaning as the single endpoint — not a definitive miss, retry the contact later. Distinct from not_found.
  • error — terminal failure for this row; error_code is one of invalid_input (name/domain unparseable), upstream_failed (verifier failure on this domain), or incomplete (the batch finished but this row's domain group exhausted its retries).

Batches are retained for 30 days after creation.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

API key authentication using Bearer token. Format: sk_live_ followed by a secure random string.

In: header

Path Parameters

id*string

The batch_id returned by the submit call.

Formatuuid

Response 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    }  ]}

On this page