Email Finder API — Getting Started — FetchLayer Docs

Getting Started

One target in, one graded answer out. No account anywhere else — just your FetchLayer key.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. The same key works on every platform.

Create free account →

2. Find an address

A bare domain is fine. So is a social profile URL or a YouTube channel.

bash
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"}'

That returned psf@python.org at confidence: "high" in 9.5 seconds on 17 September 2026, with sourceUrl: "https://www.python.org/about" — the page it was read off.

3. Read the grade, not just the address

high means the target published it themselves

Confidence is graded on who published an address, not on what it ends in. An address found anywhere other than the target’s own pages cannot grade high, however right the domain looks. stripe.com returned an address that genuinely is @stripe.com, found elsewhere on the web, and graded medium. That is the rule working.

Why the rule exists

A lookup once returned jane.diaz@stripe.com at high confidence — Stripe’s own fictional demo persona from their checkout examples. A plausible address on a perfect domain, belonging to nobody. Read emailSource, sourceUrl and onTargetDomain alongside the grade, and you can check any answer by opening one page.

onTargetDomain is measured against the target you sent

For a YouTube channel that host is youtube.com, so an address published on the creator’s own website reads false and grades low. @veritasium returned veritasium@gmail.com found on veritasium.com exactly that way. Correct behaviour — and sourceUrl is what tells you it is still a good address.

4. Handle a “no” properly

Some targets publish nothing, and that is a finished answer rather than a miss.

bash
curl -X POST https://api.fetchlayer.dev/email-finder/find-email \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"target":"github.com"}'

NO_EMAIL_LISTED is a 200, and you are charged for it

The search ran; the target publishes nothing readable. github.com, g2.com and ticketmaster.com all answer this way, each with a note naming what was examined. You are billed one credit, the same as a hit, because the work is the same — what you bought is a definitive answer about that target. Do not retry it.

A 503 is the one to retry

A target that could not be read at all answers 503 with {"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. Two different statuses on purpose: that is what makes the 200 worth trusting.

5. Check whether the answer is whole

searched[] lists what was examined, incomplete[] lists anything that could not be finished, and complete summarises the two.

Which inputs are strong

Websites and YouTube channels — 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: https://x.com/paulg came back a 200 with complete: false and incomplete: ["web-search"]. Read complete rather than inferring wholeness from how long the call took.

6. Triage a list before spending on it

Free, fetches nothing, and tells you what kind of target you have and what a lookup would examine.

bash
curl -X POST https://api.fetchlayer.dev/email-finder/resolve-target \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"target":"https://x.com/naval"}'

Answers in about a tenth of a second with targetKind, resolvedUrl, wouldSearch[] and pagesFetched: 0.

Speed and billing

A lookup took between 9.5 and 101.6 seconds across the targets measured, driven by how much there is to look through rather than by the kind of target. Budget against the top of that range and run lookups in the background.

One credit per completed lookup, hit or not, reported as pagesFetched: 1. /resolve-target is free. pagesCrawled (1 to 65 across the same set) shows how big a search was and prices nothing.

Errors and retries

A 400 names the field — a missing target, or an unrecognised key. A 401 means the key is missing or invalid. A 503 means the target could not be read; that is the one worth retrying. A NO_EMAIL_LISTED is not an error and should not be retried.

Error responses

Every error, on every route, is JSON in one shape: error (what went wrong, in words), status, code, retryable and requestId, which also comes as the X-Request-Id header. Branch on code and retryable rather than on the text; on a 5xx the text is generic. Quote requestId when reporting a problem.

json
{
  "error": "Missing required parameter: url",
  "status": 400,
  "missing": ["url"],
  "code": "invalid_input",
  "retryable": false,
  "requestId": "922874e3-c0d0-40d8-8866-d198af132420"
}
codeStatusRetryableMeaning
invalid_input400noThe request needs fixing; error names the field. A missing required parameter also lists it in missing.
unauthorized401noThe API key is missing or not valid.
insufficient_credits402noNothing left to spend: no credits, a used-up free trial, or a monthly allowance and credit balance both spent. error says which. Waiting does not help; topping up does.
forbidden403noThis key or account is not allowed this request.
not_found404noWhat the request names does not exist or is not public.
expired410noWhat the request refers to is no longer available; ask for it afresh.
quota_exceeded429noA request-rate limit was reached; error says which. Slow down before sending more.
cancelled499noThe caller closed or cancelled the request before it finished. The work was stopped and nothing was charged.
time_limit503yesYour timeoutMs ran out before there was anything to return; ask again, or allow more time.
temporarily_unavailable503yesNot available right now; ask again shortly.
posts_unavailable403noTikTok user videos: TikTok is not showing this account’s posts publicly right now. Asking again at once returns the same answer; nothing is charged.
store_unreachable503yesStore routes (Shopify, WooCommerce): the store itself can’t be reached right now — it may be offline. Ask again later. Where a store route doesn’t say so yet, an offline store is temporarily_unavailable.
upstream_error502yesThe source answered in a way that could not be used; ask again later.
internal_error500yesSomething failed on our side; ask again later, and quote requestId if it persists.
tool_error—noMCP only: a tool call failed in a way no other code describes; error says what.

More codes may be added; treat one you do not recognise by its retryable flag. Over MCP, a tool error carries the same fields in structuredContent, and as a JSON text block beside the message.

Next steps

Product overviewEmail Finder API: what it returns, pricing and use cases