Best Email Finder API in 2026: Auth, Limits, and What Breaks in Production

Six email finder and verification APIs compared on authentication, credit semantics, rate limits, async patterns, and error codes — with the failure modes that only show up after you ship. Docs and pricing verified August 2026.

You are not choosing a data vendor. You are choosing a billing model, a latency profile, and a set of error codes that your HTTP client has to survive at three in the morning. Two APIs on this page return the same email address for the same input, charge within a cent of each other, and will still produce completely different incident reports: one answers in 300ms and quietly bills you for a result you can’t send to, the other takes 90 seconds, charges nothing when it fails, and will blow through your default client timeout on the first call.

This page compares the mechanics. Endpoints, auth, what a miss costs, what a retry costs, documented limits, and the code that holds it together.

What this is and isn't. Every endpoint, header, status code and credit rule below was read off the vendor's own API documentation on 11 August 2026, and the docs URL is linked in each entry so you can check it. This is a mechanics-and-cost comparison, not a latency benchmark: we did not hold paid keys on all six providers, so there are no p95 numbers here, and anyone quoting them without publishing their method is guessing. Data-quality figures come from our own 500-contact accuracy benchmark, which covered four of these six providers.

Pick by Integration Shape, Not by Vendor

The shape of your integration eliminates most of the field before you compare features.

What you’re buildingUseWhy
Enrichment inside a signup or lead formA verification API (ZeroBounce), not a finderYou need a sub-second answer on an address you already have. Finders that verify by SMTP can take a minute or more.
Nightly CRM enrichment jobDropcontact or Anymailfinder bulkAsync job APIs. Submit a batch, poll or receive a callback, no long-lived connections.
Per-user lookups inside your productFindymail or AnymailfinderBoth charge only for results they can verify, so your unit economics track your customers’ success, not their attempts.
Mapping every contact at an accountHunter Domain SearchOne request returns everyone known at a domain, with sources and the detected pattern.
Maximum coverage across a large listApollo, or a waterfall of two or threeHighest single-provider coverage in our benchmark, at the cost of deliverability.
Any of the above, in the EUDropcontact, or ZeroBounce’s EU endpointsRegional processing endpoints and no resold database, respectively.

The Comparison, in Developer Terms

Read off each vendor's API documentation on 11 August 2026. "Charged on miss" is the single most consequential column and the one least visible on pricing pages.
API Base URL Auth Charged on miss? Documented limits Async / bulk Spec & tooling
Findymail app.findymail.com Authorization: Bearer No on finder endpoints; yes on /api/verify, which spends a credit on every attempt 300 concurrent requests Webhooks for async operations OpenAPI spec, Postman collection
Hunter api.hunter.io/v2 Query param, X-API-KEY, or Bearer Not documented for Email Finder. Domain Finder and Discover previews are explicitly free Per endpoint, and the tightest here: Domain Search 15/s and 500/min, Discover 5/s and 50/min Bulk lead operations, webhooks API wrappers, MCP server
Anymailfinder api.anymailfinder.com/v5.1 Authorization: <key>, no scheme prefix No — charged only when a valid email is found, and repeat searches are free for 30 days None documented; the vendor states it auto-scales Bulk by JSON or file, plus per-request webhooks Samples for cURL, Node, Python, PHP, Clay, n8n, Make, Zapier
Apollo api.apollo.io/api/v1 x-api-key header only Yes — you're buying stored records, not verified results Per plan and per endpoint, with minute, hour and day windows returned in response headers Bulk people and organization enrichment OpenAPI spec, MCP, CLI
Dropcontact api.dropcontact.com/v1 X-Access-Token header No — pay on success Not published; the job API paces you instead Async by design: POST a batch, poll for the result Batch JSON API, CRM integrations
ZeroBounce api.zerobounce.net/v2 (plus api-us, api-eu) api_key query param Verification always consumes a credit 80,000 requests/hour, 100,000 on ZeroBounce ONE, then a 1-day block Bulk file validation, real-time single checks Regional endpoints for data residency

Six Things That Break After You Ship

1. Your client timeout is shorter than their SMTP check

A finder that verifies at lookup has to open a connection to the recipient’s mail server, and sometimes to a catch-all detector behind it. Anymailfinder’s documentation recommends a 180-second timeout and says response time depends on the target’s SMTP server and website responsiveness. That is not a bug, it’s the mechanism you’re paying for.

Now consider the defaults you’re running: requests has no timeout at all unless you set one, which turns a slow lookup into a hung worker. httpx defaults to 5 seconds. Axios has no default timeout but every ingress in front of your service does — nginx gives you 60 seconds, most serverless platforms 30 or less, and API Gateway hard-stops at 29.

So a verified-only finder cannot live inside a synchronous request path. Either move it to a job queue with a long read timeout, or use the webhook mode these APIs provide.

Diagram comparing two integration shapes. In the synchronous shape your service posts to the find-email endpoint and waits while the provider runs an SMTP check of up to 180 seconds, but the gateway times out at 30 seconds and the connection dies before the result arrives. In the webhook shape the provider returns 200 OK in under a second, runs the check in the background, and posts the result to your callback URL later.
The same lookup, two shapes. The top one fails in staging the first time someone tests a slow mail server.
import httpx

# Read timeout has to exceed the provider's worst case, not their median.
timeout = httpx.Timeout(connect=5.0, read=190.0, write=10.0, pool=5.0)
client = httpx.AsyncClient(timeout=timeout)

2. Retries are billable

Every naive retry loop is a duplicate charge waiting to happen, and the providers differ on whether they eat that cost for you:

  • Anymailfinder makes repeated searches free for 30 days, so a retry storm costs you nothing but time.
  • Findymail doesn’t charge for duplicates, and its finder endpoints only bill when they return something.
  • Apollo and ZeroBounce bill per call. A retry of a successful-but-timed-out request is a second charge for the same answer.

The rule that follows: never retry on a timeout unless you know the provider deduplicates. If you don’t, and the response might have been produced but lost, record the attempt and reconcile from the provider’s usage endpoint rather than firing again.

3. 402, 403 and 429 mean three different things

Most clients collapse all non-2xx into “retry with backoff”, which is exactly wrong for the first one:

StatusMeaning hereCorrect response
402Findymail: out of creditsStop. Page someone. Backoff will never fix it.
423Findymail: subscription pausedStop, and alert billing.
403Hunter: rate limit reachedBack off and retry.
429Hunter: monthly usage limit reachedStop for this cycle; retrying burns nothing but wastes your workers.
5xxProvider-sideExponential backoff with jitter.

Note the Hunter pair specifically. Its documentation assigns rate limiting to 403 and usage exhaustion to 429, which is the reverse of what most engineers assume. A client that backs off on 429 and dies on 403 will do precisely the wrong thing on both.

4. “Not found” is a 200, and every provider shapes it differently

None of these APIs return 404 for a person they couldn’t find, because the request succeeded — the answer is just empty. The shapes diverge:

// Hunter — Domain Search, no results
{ "data": { "domain": null, "pattern": null, "emails": [] },
  "meta": { "results": 0 } }

// Findymail — Email Finder, hit
{ "contact": { "name": "Elon Musk", "email": "[email protected]", "domain": "tesla.com" } }
// ...and on a miss, no contact object to read through

// Anymail Finder — two email fields, and they are not interchangeable
{ "credits_charged": 1,
  "email": "[email protected]",        // may be a RISKY address
  "email_status": "valid",                   // valid | risky | not_found | blacklisted
  "valid_email": "[email protected]" }  // populated ONLY when status is valid

Write your adapters so a miss is a None return, not an exception, and so a schema change surfaces as a loud parse failure rather than a silent null that quietly empties your CRM column.

And read that last one carefully, because it’s the sharpest edge on this page. Anymail Finder returns risky addresses in email and charges nothing for them, precisely because they might bounce. If your adapter reads email you will cheerfully pipe unverified addresses into your sequences and wonder why a pay-only-for-valid provider is bouncing. Read valid_email, or gate on email_status == "valid".

Two of these APIs also hand you cost telemetry for free: Anymail Finder returns credits_charged on every response, and Dropcontact returns credits_left on job submission. Log both. It’s cheaper than reconciling an invoice.

5. Concurrency limits are not rate limits

Findymail documents 300 concurrent requests, not 300 requests per second. Those govern different things: one caps how many connections you may hold open at once, the other caps throughput over time. With a mean latency of two seconds, a 300-concurrency budget is roughly 150 requests per second — but with a 60-second SMTP check on a slow domain, the same budget is five.

Size your worker pool against concurrency, and your queue drain rate against observed latency. A semaphore is the right primitive; a token bucket alone is not.

Hunter, by contrast, publishes true rate limits, and per endpoint: Domain Search allows 15 requests per second and 500 per minute, while Discover allows 5 per second and 50 per minute. If you’re fanning out across endpoints, you need a limiter per endpoint, not one global bucket.

6. Where the bytes are processed is a design decision, not a footnote

If you have EU data subjects, this belongs in your architecture review rather than your vendor questionnaire:

  • ZeroBounce publishes regional endpoints — api-us.zerobounce.net and api-eu.zerobounce.net — and warns explicitly that the default endpoints process data on US servers.
  • Dropcontact holds no contact database at all, computing and verifying addresses instead, and is audited against CNIL standards. That’s a materially shorter answer to give your DPO than “we query a vendor’s database of scraped profiles.”
  • Findymail documents that its phone finder excludes the EU for GDPR reasons, so /api/search/phone will return nothing for European prospects by design, not by failure.

Getting this wrong isn’t a latency problem, it’s a re-architecture six months in.

The APIs, One by One

Findymail — the only one that puts a number in the contract

API docs · base URL https://app.findymail.com

Bearer auth, JSON in and out, and three things that matter more than the interface.

It is the only API here that guarantees an outcome. Findymail publishes a bounce-rate guarantee: stay above 5% and it refunds the credits. Every other provider on this page sells you attempts, or at best verified attempts; this one takes a position on the result and puts money behind it. For anyone reselling lookups inside a product, that’s the difference between a supplier and a dependency you have to hedge.

It has the highest documented parallelism here — 300 concurrent requests, against Hunter’s 5 to 15 per second. You can saturate a worker pool without building a limiter first, which is usually a day of work you don’t do.

The credit rules are unambiguous and in your favour: finder endpoints bill only when they return a contact, duplicates aren’t charged, and unused credits roll over up to 2× your plan. Compare that against having to email Hunter’s support to find out what a miss costs.

In our benchmark it was also the most accurate provider tested, at 93.2% with a 1.2% bounce rate — which in an API context is a cost argument, not a quality one. At Apollo’s 7.2% bounce rate you need a verification pass on everything you retrieve, so your integration carries a second vendor, a second failure mode, and about $10 more per thousand. At 1.2% you can ship the addresses straight into a sequence.

curl -X POST "https://app.findymail.com/api/search/name" \
  -H "Authorization: Bearer $FINDYMAIL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Elon Musk", "domain": "tesla.com"}'
# 200 → { "contact": { "name": "...", "email": "[email protected]", "domain": "tesla.com" } }

Endpoints worth knowing: /api/search/name (1 credit when found), /api/search/phone by LinkedIn URL (10 credits, EU excluded), /api/search/company, /api/search/employees (1 credit per contact returned), /api/search/reverse-email (1 credit, 2 with full profile), /api/intellimatch/search for natural-language lead search, and /api/verify.

The gotchas, and they’re real: /api/verify spends a verifier credit on every attempted verification, unlike the finder endpoints — the one asymmetry in an otherwise consistent billing model. The phone endpoint returns nothing for EU prospects by design, so European dialing lists need a different vendor entirely. And the headline per-credit price is the highest of the finders here at roughly $0.049, which only pays for itself if you value the guarantee and the accuracy over raw volume.

Errors: 402 for insufficient credits, 423 for a paused subscription. Both are terminal — stop, don’t retry.

Tooling: OpenAPI spec and a Postman collection are published, and the docs carry a last-updated date, which is more than most vendors manage.

Hunter — the nicest surface, the tightest limits

API docs · base URL https://api.hunter.io/v2/

Graded on the parts you touch in an editor, Hunter is the most pleasant API here. Consistent { data, meta } envelope on success and { errors } on failure. Three accepted auth methods. A published error taxonomy. Official wrappers and an MCP server.

curl "https://api.hunter.io/v2/email-finder?domain=intercom.com&first_name=Eoghan&last_name=McCabe" \
  -H "X-API-KEY: $HUNTER_KEY"

Two design details worth stealing: Domain Finder, which resolves a company name to a domain, is free and doesn’t decrement your quota. And Discover People returns full metadata with the address masked behind a reveal_handle, letting you survey what exists for free and spend credits only on the rows you want. Results paginate by cursor (search_after), not offset, so deep pagination stays consistent.

Then you try to run volume through it, and three things bite.

The rate limits are the tightest on this page. Discover allows 5 requests per second and 50 per minute; Domain Search allows 15 per second and 500 per minute. At 50 requests a minute, a 10,000-row enrichment through Discover takes over three hours of wall-clock time before you’ve handled a single retry. Findymail documents 300 concurrent requests and Anymail Finder documents no limit at all. If your job is bulk, Hunter’s ceiling is the first thing you’ll hit and the hardest to engineer around.

The docs don’t say whether a failed lookup costs a credit. They go out of their way to mark Domain Finder as free and Discover previews as free, which makes the silence everywhere else conspicuous. For a data API, “what does a miss cost me?” is the first question a buyer asks, and the API reference that’s otherwise the best-documented here doesn’t answer it. Assume you’re paying for misses until support tells you otherwise, and note that this is precisely the question Findymail and Anymail Finder answer in the first line of their pricing docs.

Its misses are frequent. 72.6% coverage in our benchmark, against 83.2% for Findymail and 88.2% for Apollo. In a UI that’s a shrug; in a waterfall it means more than a quarter of your rows fall through to a second paid provider, so the cheap credit isn’t the cheap answer.

Also worth knowing: the 403/429 inversion described above, and euro pricing, which quietly drifts your per-credit cost with the exchange rate if you bill in dollars.

Data quality: 84.7% accuracy and 4.8% bounce — reliable when it answers. See our Hunter guide and Hunter alternatives.

Anymailfinder — built around the billing promise

API docs · base URL https://api.anymailfinder.com/v5.1

The whole API is arranged around one rule: you pay one credit when a valid email is found, and nothing when the result is risky, blacklisted or absent. Repeat searches inside 30 days are free. Four find endpoints — /find-email/person, /company, /linkedin-url, /decision-maker — plus bulk by JSON or file, and a verify endpoint.

curl -X POST "https://api.anymailfinder.com/v5.1/find-email/person" \
  -H "Authorization: $ANYMAIL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "microsoft.com", "full_name": "John Doe"}'

Note the auth header takes the raw key with no Bearer prefix — a five-minute debugging detour if you’ve just come from another provider. You can pass linkedin_url alone, or with name and company, in which case it runs the name lookup first and falls back to the profile.

The response carries email_status (valid, risky, not_found, blacklisted), the MX host it verified against, and credits_charged. Take the address from valid_email rather than email, for the reason described above.

The gotcha: no documented rate limits, and a recommended 180-second timeout. Freedom to hammer it plus long tail latency means your own infrastructure is the constraint. Use the webhook mode — pass x-webhook-url and the call returns immediately, with the result POSTed to you later — for anything above trivial volume.

Not in our benchmark, so the vendor’s coverage and accuracy claims are unaudited by us.

Apollo — coverage, at the cost of verification

API docs · base URL https://api.apollo.io/api/v1

Authentication is x-api-key header-only; query and body parameters stopped working in 2024, which still catches out old integrations and older tutorials. The surface is enormous — people and organization search, enrichment, bulk enrichment, plus full CRUD over accounts, contacts, deals, sequences, calls and tasks — and there’s an OpenAPI spec, a CLI and an MCP server.

Rate limits are per plan and per endpoint across minute, hour and day windows, and the current values come back in response headers rather than as published numbers. Read the headers; don’t hardcode a guess.

The gotcha: you’re buying stored records. In our benchmark Apollo had the best coverage of any tool at 88.2%, and 7.2% of what it returned hard-bounced, with 18.7% sitting on unverifiable catch-all domains. Budget for a verification step on everything you retrieve, or you’re shipping a bounce generator. See Apollo alternatives if that trade is why you’re here.

Dropcontact — an async job API, and a GDPR position

API docs · base URL https://api.dropcontact.com/v1

The only provider here whose primary flow is genuinely asynchronous, which makes it the easiest to run as a scheduled job and the most awkward to call from a request handler.

import requests

# 1. Submit the batch
r = requests.post(
    "https://api.dropcontact.com/v1/enrich/all",
    json={"data": [{"first_name": "John", "last_name": "Smith", "website": "corporation.com"}],
          "siren": True, "language": "en"},
    headers={"Content-Type": "application/json", "X-Access-Token": API_KEY},
)
request_id = r.json()["request_id"]          # also returns credits_left

# 2. Poll until it's done — the API tells you to wait 30 seconds between attempts
res = requests.get(f"https://api.dropcontact.com/v1/enrich/all/{request_id}",
                   headers={"X-Access-Token": API_KEY}).json()
# {"error": false, "reason": "Request not ready yet, try again in 30 seconds", "success": false}

The response returns credits_left on submission, which is the cleanest credit-observability of any API here: you can alert on depletion without a separate usage call. Emails come back with a qualification field (nominative@pro and friends) rather than a bare confidence score, which is more useful than a percentage once you’re writing routing rules.

The gotcha: polling. Budget for a job runner with persistence, because a request_id you lose is work you paid for and can’t collect.

ZeroBounce — verification, with regional endpoints

API docs · base URL https://api.zerobounce.net/v2

Not a finder — reach for it when you already have addresses and need to know which are safe to send. Rate limits are unusually explicit: 80,000 requests an hour, 100,000 for ZeroBounce ONE customers, and exceeding it earns a one-day block rather than a 429. Two hundred bad-API-key requests in an hour buys the same one-day block, which means a misconfigured staging deploy can lock out production.

The api-us and api-eu hostnames let you pin processing to a region, and the docs are explicit that the default endpoints run on US servers.

Entry pricing is $99 a month for 10,000 validation credits, so roughly a cent a check, against two to five cents for a finder lookup. If the job is “clean a list I already own”, buying finder credits for it is a straight overspend. More options in our verification comparison.

A Waterfall That Survives Contact With Production

The single-provider ceiling in our testing was 88.2% coverage, so any serious pipeline chains providers. Here is the pattern with the failure modes above actually handled: long read timeout, jittered backoff, terminal handling for credit exhaustion, concurrency capped by semaphore rather than by hope, and misses returned as None instead of raised.

import asyncio, os, random
from typing import Optional
import httpx

class OutOfCredits(RuntimeError):
    """Terminal: no amount of retrying makes money appear."""

TERMINAL = {402, 423}          # Findymail: no credits / paused subscription
BACKOFF  = {403, 429, 500, 502, 503, 504}   # Hunter puts rate limiting on 403

async def _request(client, method, url, *, attempts=4, **kw) -> Optional[dict]:
    for attempt in range(attempts):
        r = await client.request(method, url, **kw)
        if r.status_code in TERMINAL:
            raise OutOfCredits(f"{url} returned {r.status_code}")
        if r.status_code in BACKOFF:
            wait = float(r.headers.get("Retry-After") or 0) or 2 ** attempt + random.random()
            await asyncio.sleep(wait)
            continue
        if r.status_code == 404:
            return None
        r.raise_for_status()
        return r.json()
    return None

async def findymail(client, name: str, domain: str) -> Optional[str]:
    body = await _request(client, "POST", "https://app.findymail.com/api/search/name",
                          headers={"Authorization": f"Bearer {os.environ['FINDYMAIL_KEY']}"},
                          json={"name": name, "domain": domain})
    return ((body or {}).get("contact") or {}).get("email")

async def anymailfinder(client, name: str, domain: str) -> Optional[str]:
    body = await _request(client, "POST", "https://api.anymailfinder.com/v5.1/find-email/person",
                          headers={"Authorization": os.environ["ANYMAIL_KEY"]},   # no Bearer prefix
                          json={"full_name": name, "domain": domain})
    # valid_email is null unless email_status == "valid"; `email` can hold a risky address
    return (body or {}).get("valid_email")

async def hunter(client, name: str, domain: str) -> Optional[str]:
    first, _, last = name.partition(" ")
    body = await _request(client, "GET", "https://api.hunter.io/v2/email-finder",
                          headers={"X-API-KEY": os.environ["HUNTER_KEY"]},
                          params={"domain": domain, "first_name": first, "last_name": last})
    return ((body or {}).get("data") or {}).get("email")

PROVIDERS = (findymail, anymailfinder, hunter)   # accuracy first, then price per credit

async def resolve(client, sem, name: str, domain: str) -> dict:
    async with sem:                                  # concurrency, not throughput
        for provider in PROVIDERS:
            try:
                email = await provider(client, name, domain)
            except OutOfCredits:
                continue                             # skip this provider for the rest of the run
            except httpx.HTTPError:
                continue                             # one flaky vendor shouldn't sink the row
            if email:
                return {"name": name, "domain": domain,
                        "email": email, "source": provider.__name__}
    return {"name": name, "domain": domain, "email": None, "source": None}

async def main(rows):
    timeout = httpx.Timeout(connect=5.0, read=190.0, write=10.0, pool=5.0)
    sem = asyncio.Semaphore(50)                      # well under Findymail's 300 concurrent
    async with httpx.AsyncClient(timeout=timeout) as client:
        return await asyncio.gather(*(resolve(client, sem, r["name"], r["domain"]) for r in rows))

Three things this deliberately does not do, all of which you should add before it touches real volume. It doesn’t persist attempts, so a crash mid-run re-spends credits at providers that bill per call. It doesn’t cache by (name, domain), which is the cheapest optimisation available given how often the same contact turns up in two lists. And it doesn’t check each vendor’s terms on storing returned data, which vary and are worth reading before you build a cache with a long TTL.

Our waterfall enrichment guide covers the ordering logic and the coverage maths in more depth.

What It Actually Costs Per Thousand Usable Emails

Price per credit is not price per usable email, and most comparisons of these APIs — including the first draft of this one — quietly cheat by putting one vendor’s annual-equivalent rate next to another’s monthly price. Hunter’s €34 and Apollo’s $49 are annual rates; Findymail’s $49 is what you pay month to month. Compared on the same basis, the gaps look different.

Everything below is the month-to-month price, which is what you actually pay in the first month of an integration, with the annual-equivalent rate in brackets. Verified 11 August 2026.

APIEntry price, billed monthlyPer creditBilled on missAccuracy → deliverableCost per 1,000 sendable
Findymail$49/mo, 1,000 credits (~$41 annualised)$0.049No93.2%, 1.2% bounce~$53
Hunter€49/mo, 2,000 credits (€34 annualised)€0.025Undocumented for Email Finder84.7%, 4.8% bounce~€31
Anymail Finder€312/yr equivalent, 12,000 credits (€204/yr for 4,800 if you commit)€0.026 monthly, €0.007 at volumeNoNot tested by us€28 at face value
Apollo~$63/seat/mo, 30,000 credits/yr ($49 annualised)~$0.025Yes81.3%, 7.2% bounce~$33, plus verification
ZeroBounce$99/mo, 10,000 credits~$0.010YesVerification only~$10 per 1,000 checks

Three caveats on that table. The “cost per 1,000 sendable” column divides the credit price by accuracy and by one minus the bounce rate, which assumes your list resembles our 500-contact test set — it doesn’t, so read the column as a ranking, not a quote. Apollo’s figure excludes the verification pass its bounce rate obliges, roughly $10 per thousand more, which closes most of the remaining gap. And Anymail Finder’s monthly pricing is a 33% premium over its annual bundles, so the volume rates in its favour need a year’s commitment to reach.

On a like-for-like monthly basis the per-credit spread across the finders is about 2×, not the 3× you get from comparing an annual rate to a monthly one. That is small enough that credit price should not decide this. What should decide it: whether misses are billable, whether you need a second vendor to verify what the first one sold you, and whether anyone will refund you when the data is wrong.

Frequently Asked, by People Who Have to Ship This

Which email finder API is best for a SaaS product that resells lookups? One that only bills for verified results, so your cost of goods tracks your customers’ success rather than their attempts — Findymail or Anymailfinder. Both also skip duplicate charges, which matters when several of your customers look up the same contact.

Can I call an email finder API from a request handler? Not a verifying one. Anymailfinder documents a 180-second timeout recommendation, and any SMTP-based verifier can take that long on a slow mail server. Queue it, or use webhook mode. Verification APIs like ZeroBounce are fast enough for synchronous use.

What’s the difference between concurrency limits and rate limits? Concurrency caps simultaneous in-flight requests (Findymail: 300). Rate limits cap requests per unit time (Hunter: 15/s and 500/min on Domain Search). Under long latency they imply very different throughput, so build against both: a semaphore for the first, a limiter for the second.

Do these APIs charge when they find nothing? It splits the field. Findymail’s finder endpoints, Anymailfinder and Dropcontact charge only on success. Apollo and ZeroBounce bill per call. Findymail’s /api/verify bills on every attempt even though its finders don’t — the one asymmetry in that group.

Which one has the best documentation? Hunter for structure — consistent envelope, three auth methods, per-endpoint rate limits, a published error taxonomy, wrappers and an MCP server. Anymail Finder for completeness: it documents the full response schema, every email_status value, the credit rule and a timeout recommendation, which is the set of things you actually need before writing an adapter. Hunter, for all its polish, never says what a failed Email Finder lookup costs.

Is there a free tier I can build against? Yes, and they’re real: Hunter gives 50 credits a month, Apollo 900 per seat per year, ZeroBounce 100 validations a month plus 10 finder lookups, Dropcontact 50 credits, Findymail 25 on signup. Enough to write the adapter and the tests; not enough to benchmark data quality, which needs about 100 contacts you already know the answers for.

How do I handle GDPR with these APIs? Decide where processing happens before you pick. ZeroBounce publishes EU endpoints; Dropcontact computes rather than resells and is audited to CNIL standards; Findymail’s phone endpoint excludes the EU by design. Then read each vendor’s terms on storing returned data before you cache it, because that’s the part people get wrong at the architecture level rather than the vendor level.