Email Finder API v1
One target in, one graded answer out, from https://api.fetchlayer.dev/email-finder. A website, a social profile or a YouTube channel — and an answer either way, including the answer that there is nothing to find.
“No email” is an answer, not a failure
status: "NO_EMAIL_LISTED" comes back as a 200. It means the search ran and the target publishes no readable contact address — and for large companies that is the ordinary outcome, not an edge case. Measured on 17 September 2026: github.com, g2.com and ticketmaster.com all answered this way, each with a note naming what was examined. 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: {"error":"the target could not be read right now, please retry"} — indeed.com returned one in 6.2s, and a domain that does not resolve in 2.8s.
That separation is the whole point. Retry the 503; take the 200. A negative result you can rely on is what lets you close a row out instead of re-running it forever.
Confidence is graded on who published it
An address the target published on its own pages can grade high. An address found anywhere else cannot — however right the domain looks.
| Target | Address | Published by | Grade |
|---|---|---|---|
| python.org | psf@python.org | its own /about page | high |
| stripe.com | …@stripe.com | somewhere else on the web | medium |
| x.com/naval | an address on a third party’s site | somewhere else on the web | low |
The stripe.com row is the rule working, not a bug: the address really is @stripe.com, and it still grades medium because Stripe did not publish it where it was found.
The rule exists because a plausible address on a perfect domain can belong to nobody. A lookup once returned jane.diaz@stripe.com at high confidence — Stripe’s own fictional demo persona from their checkout examples. Grading on who published it rather than on what it ends in is what rules that class of answer out.
Every response carries sourceUrl, so a grade is never something you have to take on faith.
One nuance in onTargetDomain: it is measured against the target as you gave it. For a YouTube channel that 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, graded low. That is correct; sourceUrl is what tells you it is nonetheless a good address.
Available endpoints
Whole, partial, or failed — the response says which
searched[] lists what was examined, incomplete[] lists anything that could not be finished, and complete summarises the two. Read complete rather than inferring wholeness from how long a call took.
Websites and YouTube channels are the strong inputs — every one measured returned a complete answer. Social profiles 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: a 200, complete: false, incomplete: ["web-search"], with a note saying so and that retrying may cover more.
How long a lookup takes
Between about 10 and 100 seconds for a 200, measured across websites, X profiles and YouTube channels on 17 September 2026 — websites 9.5–101.6s, YouTube 50.9–77.6s, social 19.6–95.8s. It is driven by how much there is to look through for a given target rather than by the kind of target, so it does not sort neatly by input type.
Budget timeouts against the top of that range, and run lookups in the background rather than inside a request/response cycle.
Billing
One credit per completed lookup — including a NO_EMAIL_LISTED. The search is the work, and it is identical either way. What you are buying is a definitive answer about a target, not a lottery ticket on one; a “no” you can rely on is the result that lets you stop working a row.
Every completed lookup reports pagesFetched: 1. /resolve-target reports 0 and is free. A 503 fetched nothing.
pagesCrawled is not a price. It ranged from 1 to 65 across the targets measured, and it is there so two targets that both cost one credit are not indistinguishable when one answered immediately and the other did not.
Errors
- 400 — the request is wrong, and the message names the field: “target: Invalid input: expected string, received undefined” for a missing target, “Unrecognized key: …” for an unknown one. The body is a strict object, so a typo is rejected rather than ignored.
- 401 — missing or invalid key.
- 503 — the target could not be read. This is the one to retry. A target that publishes nothing is a 200, not this.