Find a contact email
Find the public contact email a target publishes. `target` is a website (`python.org`), a social profile URL (`https://x.com/naval`) or a YouTube channel (`https://www.youtube.com/@mkbhd`). **`NO_EMAIL_LISTED` is a successful answer, not a failure.** It means the search ran and the target publishes no readable address — and that is the common case for big, well-run companies, not the exception: github.com, g2.com and ticketmaster.com all answer this way, each with a note naming every stage that was checked. Stripe publishes a support portal rather than an address. The failure case has its own status: a target that could not be read at all is a **503** — indeed.com returned one in 6.2s, and a domain that does not resolve in 2.8s, both with `{"error":"the target could not be read right now, please retry"}`. That separation is what makes a "no" worth trusting: retry the 503, and take the `NO_EMAIL_LISTED` as the answer it is. **`confidence` is gated on provenance, not on the domain.** An address the target did not publish on its own pages can never grade `high`, however right the domain looks. Measured: `python.org` returned `psf@python.org` from the target's own /about page at **high**; `stripe.com` returned an address that really is `@stripe.com`, found on a Play Store listing, at **medium** — on the right domain and still not high, because Stripe did not publish it there. The rule exists because a lookup once returned `jane.diaz@stripe.com` at high confidence: Stripe's own fictional demo persona from their checkout mock-ups, a plausible address on a perfect domain belonging to nobody. Grading on where an address was published rather than on what it ends in is what stops that class of answer. Read `emailSource`, `sourceUrl` and `onTargetDomain` alongside the grade rather than the grade alone. **The response says what it looked at.** `searched[]` lists the stages that ran, `incomplete[]` lists any that could not finish, and `complete` summarises the two — so a partial answer is always distinguishable from a failed one, and from a whole one. **Which inputs are strong:** websites and YouTube channels. Every website and YouTube target measured returned a complete answer. Social lookups are supported, can take longer, and may return a partial result, which the response marks as incomplete — the one partial observed was `https://x.com/paulg` at 95.8s, with `complete: false` and `incomplete: ["web-search"]`. **Latency** is driven by how far a target pushes the search rather than by the kind of target alone, so it does not sort neatly by input type: 9.5s to 101.6s measured for a 200 (websites 9.5-101.6s, YouTube 50.9-77.6s, social 19.6-95.8s). Budget timeouts against the top of that range. **Billing: one credit per completed lookup, including a `NO_EMAIL_LISTED`.** The same work runs either way — the same pages are fetched, the same stages run — and what you are paying for is the search, not the address. `pagesCrawled` (1 to 65 across the set measured) is there so the size of a search is visible; it prices nothing.
/find-email Lookup https://api.fetchlayer.dev/email-finder/find-email Headers
Authorization string required Bearer token — your FetchLayer API key.
Parameters
target string required A website (python.org), a social profile URL (https://x.com/naval) or a YouTube channel (https://www.youtube.com/@mkbhd). A bare domain is fine.
format string optional Envelope format: json or markdown. (default: json)
Response Fields
find-email
Find a contact email
transport string The source. "public-web".
notes string[] Caveats you can act on, in plain language: which stages were checked and found nothing, that an address came from somewhere other than the target's own pages, or that a stage could not be finished in the time available.
pagesFetched number What the request is billed at: 1 on every completed lookup, including a NO_EMAIL_LISTED; 0 on /resolve-target. It is a billing figure, not a crawl size — that is `pagesCrawled`.
input string The target exactly as you sent it.
normalizedInput string The target after normalisation.
targetKind string What the target was taken to be: website, social, youtube.
resolvedUrl string The page the search started from. For a YouTube channel this is its /about page.
entityName string The name of whatever the target turned out to be, where one could be read. Null on an X profile, which publishes none to this reader.
entityHandle string The handle, on a social or YouTube target, e.g. "@mkbhd".
email string The address found, or null on a NO_EMAIL_LISTED.
emailSource string Where it was found: "website" (the target's own pages) or "web-search" (a page elsewhere naming the target). This, not the domain, is what caps `confidence`.
sourceUrl string The exact page the address was read off, so any grade can be checked by hand.
confidence string high, medium, low, or null on a NO_EMAIL_LISTED. Gated on provenance: only an address the target published on its own pages can reach "high".
onTargetDomain boolean Whether the address sits on the target's own host. Measured against the target as given: for a YouTube channel the host is youtube.com, so an address published on the creator's own website reads false — `@veritasium` returned `veritasium@gmail.com` found on veritasium.com with `onTargetDomain: false` and a `low` grade. That is correct behaviour; `sourceUrl` is what tells you it is still a good address.
status string EMAIL_FOUND or NO_EMAIL_LISTED. Both are successful answers — the failure case is a 503.
searched string[] The stages that ran, e.g. landing-page, website-pages, linked-social-profiles, web-search, and on a YouTube target recent-video-descriptions.
incomplete string[] Stages that started but could not be finished. Empty on a whole answer.
complete boolean The one-field summary of the two lists above. **Read this rather than inferring wholeness from the elapsed time.** False means some of the search did not run, and the note says which and why.
pagesCrawled number How many pages the search actually read — 1 to 65 across the set measured. Transparency only: it prices nothing, and `pagesFetched` is the billing figure.
scrapedAt string When the lookup ran, ISO 8601.
Request
curl -X POST "https://api.fetchlayer.dev/email-finder/find-email" \
-H "Authorization: Bearer ss-your-key" \
-H "Content-Type: application/json" \
-d '{"target":"python.org"}'Response
{
"transport": "public-web",
"notes": [],
"pagesFetched": 1,
"input": "python.org",
"normalizedInput": "python.org",
"targetKind": "website",
"resolvedUrl": "https://www.python.org/",
"entityName": "Welcome to Python.org",
"entityHandle": null,
"email": "psf@python.org",
"emailSource": "website",
"sourceUrl": "https://www.python.org/about",
"confidence": "high",
"onTargetDomain": true,
"status": "EMAIL_FOUND",
"searched": [
"landing-page",
"linked-social-profiles",
"website-pages"
],
"incomplete": [],
"complete": true,
"pagesCrawled": 16,
"scrapedAt": "2026-09-16T22:54:40.477Z"
}