Email Verification API
Email verification API: syntax, MX and SMTP checks in one call
POST an address to /email-verifier/verify and get a verdict (valid, invalid, accept_all or unknown) with the checks behind it: syntax, DNS and MX records, an SMTP mailbox check, and disposable and role-address flags. Each verification costs 5 credits, which is $1.50 per 1,000 at the Scale pack.
{ "searchTime": 0.17, "totalResults": 85000, "page": 1, "organic": [ { "position": 1, "title": "31.10. Connection Pools and Data Sources", "link": "www.postgresql.org/docs/…", "domain": "postgresql.org", "snippet": "For an environment without an application se…" }, { "position": 2, "domain": "medium.com", … }, { "position": 3, "domain": "stackoverflow.blog", … } ], "credits": 1}How much does an email verification API cost per 1,000 checks?
On Searlo each verification is 5 credits, so 1,000 verifications use 5,000 credits: $3.99 on the Micro pack, $1.50 at Scale-pack rates ($74.99 buys 50,000 verifications) and about $1.00 at Enterprise-pack rates ($799 for 800,000). The 3,000 free credits cover 600 verifications.
Packs are one-time purchases, not a subscription. A completed check is billed whatever the verdict, including unknown and accept_all. A request that fails with a server error or is rate limited is not charged.
- Price per check
- 5 credits— Searlo pricing
- Per 1,000 checks
- $1.00 (Enterprise) to $3.99 (Micro); $1.50 at the Scale pack
- Free to start
- 3,000 credits, or 600 checks, no card— Searlo pricing
- ZeroBounce pay-as-you-go minimum
- $39 for 2,000 validations ($19.50 per 1,000)— zerobounce.net
- Hunter Starter, monthly billing
- $49 a month for 2,000 credits; a verification is 0.5 credit— hunter.io/pricing
Third-party figures on this page last verified against the sources linked above. Prices change; if you find one of these stale, tell us and we will correct it.
Email verification answers one question before you send: will this address accept mail? Searlo's API runs its checks in order of cost. Syntax and a list of disposable domains are checked first, locally. Then the domain's DNS and MX records. Then an SMTP conversation with the domain's mail server, which asks whether the mailbox exists without sending a message. What comes back is a verdict, a 0–100 score when the checks produce one, a reason, and a details object holding every signal the checks established.
It is the programmatic version of the free email verifier on this site. The free tool is for checking an address by hand in a browser; the API is for code, such as signup and checkout forms, CRM syncs, list cleaning before a campaign, and lead pipelines.
Verification is one address per call. For bulk email verification you send calls concurrently within your account's limits (by default 100 a minute, 5,000 an hour and 20,000 a day), de-duplicate the list first, and retry the answers that say a retry is worth it.
What each verdict means
| status | What it means | What to do |
|---|---|---|
| valid | The mail server accepted this mailbox during the SMTP check | Keep it. Check details.disposable and details.roleAccount if those matter to you |
| invalid | Bad syntax, a known disposable domain, a domain with no DNS or MX, or a mailbox the server rejected (see reason) | Drop it, or ask the user to fix it; show didYouMean when it is present |
| accept_all | The domain accepts every address (catch-all), so this mailbox cannot be confirmed on its own | Keep it with care; how far to trust it is your call |
| unknown | The check did not conclude: greylisting, a blocked probe, an unreachable mail server, or a provider that accepts every recipient and bounces later | If retryable is true, try again later; otherwise treat it as unconfirmed |
| error | Rare: the check itself could not run | Handle it like unknown |
Verify addresses in cURL, Python and Node
One address per POST. The Python and Node versions de-duplicate a list, pace requests under the default rate limit, and set aside the answers worth retrying.
curl -X POST "https://api.searlo.tech/api/v1/email-verifier/verify" \
-H "x-api-key: YOUR_API_KEY" \
-H "content-type: application/json" \
-d '{ "email": "[email protected]" }'import time
import requests
from concurrent.futures import ThreadPoolExecutor
ENDPOINT = "https://api.searlo.tech/api/v1/email-verifier/verify"
HEADERS = {"x-api-key": "YOUR_API_KEY"}
PER_MINUTE = 80 # under the default limits of 100 a minute and 5,000 an hour
def verify(email):
r = requests.post(ENDPOINT, headers=HEADERS, json={"email": email}, timeout=60)
if r.status_code == 429 or r.status_code >= 500:
return email, None # not charged: retry later
if r.status_code == 400: # rejected before billing
return email, {"status": "invalid", "reason": "rejected_format"}
r.raise_for_status()
return email, r.json()["data"]
raw = ["[email protected]", "[email protected] ", "[email protected]", "[email protected]"]
emails = sorted({e.strip().lower() for e in raw}) # a repeat check is billed again
results, retry_later = {}, []
with ThreadPoolExecutor(max_workers=4) as pool:
futures = []
for email in emails:
futures.append(pool.submit(verify, email))
time.sleep(60 / PER_MINUTE) # pace requests under the limit
for future in futures:
email, data = future.result()
if data is None or data.get("retryable"):
retry_later.append(email)
else:
results[email] = data
for email, data in results.items():
print(email, data["status"], data.get("reason"), data.get("didYouMean"))
print("retry later:", retry_later)const ENDPOINT = "https://api.searlo.tech/api/v1/email-verifier/verify";
const PER_MINUTE = 80; // under the default limits of 100 a minute and 5,000 an hour
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function verify(email) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY", "content-type": "application/json" },
body: JSON.stringify({ email }),
signal: AbortSignal.timeout(60_000),
});
if (res.status === 429 || res.status >= 500) return { email, retryLater: true }; // not charged
const body = await res.json();
if (!res.ok) return { email, status: "invalid", reason: body.message }; // 400: not charged
return body.data;
}
// De-duplicate first: a repeat check of the same address is billed again.
const emails = [...new Set(
["[email protected]", "[email protected]", "[email protected]"].map((e) => e.trim().toLowerCase()),
)];
const pending = [];
for (const email of emails) {
pending.push(verify(email)); // start one check...
await sleep(60_000 / PER_MINUTE); // ...then wait, so the rate stays under the limit
}
for (const r of await Promise.all(pending)) {
if (r.retryLater || r.retryable) console.log(r.email, "retry later");
else console.log(r.email, r.status, r.reason ?? "", r.didYouMean ?? "");
}Response (illustrative)
The verdict sits in data. Values are illustrative; a signal a check could not establish comes back null, which means unknown, not false.
{
"statusCode": 200,
"data": {
"email": "[email protected]",
"status": "valid",
"domain": "example.com",
"message": "The mailbox exists and accepts mail.",
"score": 97,
"reason": "mailbox_exists",
"retryable": false,
"didYouMean": null,
"details": {
"format": "valid",
"mailboxStatus": "valid",
"mailboxType": "professional",
"mailboxExchange": "mx.example.com",
"domainStatus": "valid",
"mailProvider": "self-hosted",
"catchAll": false,
"rcptUnreliable": false,
"disposable": false,
"roleAccount": false,
"freeProvider": false,
"smtpServer": true,
"smtpCheck": true,
"mxFound": true
},
"verifiedAt": "2026-09-29T10:15:04.000Z",
"source": "searlo"
},
"message": "Email verified",
"success": true
}{
"statusCode": 200,
"data": {
"email": "[email protected]",
"status": "unknown",
"domain": "slow-mail.example",
"message": "The mail server asked us to try again later.",
"score": 40,
"reason": "greylisted",
"retryable": true,
"didYouMean": null,
"details": {
"format": "valid",
"mailboxStatus": "unknown",
"mailboxType": "role",
"mailboxExchange": "mx1.slow-mail.example",
"domainStatus": "valid",
"mailProvider": null,
"catchAll": null,
"rcptUnreliable": null,
"disposable": false,
"roleAccount": true,
"freeProvider": false,
"smtpServer": true,
"smtpCheck": null,
"mxFound": true
},
"verifiedAt": "2026-09-29T10:15:09.000Z",
"source": "searlo"
},
"message": "Email verified",
"success": true
}The checks behind every verdict
Syntax
An RFC-style format check before anything touches the network, reported in details.format.
Domain, DNS and MX
Does the domain resolve, and does it publish mail servers? details.domainStatus and details.mxFound; details.mailboxExchange names the MX.
SMTP mailbox check
A conversation with the domain's mail server that asks about this mailbox without sending mail: details.smtpServer and details.smtpCheck.
Disposable addresses
Known throwaway domains are rejected up front as invalid, with reason disposable_domain; others are flagged in details.disposable.
Role addresses
Shared inboxes such as info@, sales@ and support@ are flagged in details.roleAccount; free-mail domains in details.freeProvider.
Catch-alls and typos
details.catchAll marks domains that accept everything, and didYouMean suggests a corrected address for a likely typo.
Price per 1,000 verifications
| Pack | Price | Credits | Verifications | Per 1,000 verifications |
|---|---|---|---|---|
| Free signup credits | $0 | 3,000 | 600 | Free |
| Micro | $3.99 | 5,000 | 1,000 | $3.99 |
| Starter | $9.99 | 20,000 | 4,000 | $2.50 |
| Builder | $29.99 | 75,000 | 15,000 | $2.00 |
| Scale | $74.99 | 250,000 | 50,000 | $1.50 |
| Pro | $199.99 | 900,000 | 180,000 | $1.11 |
| Enterprise | $799 | 4,000,000 | 800,000 | $1.00 |
Pricing maths
Cleaning a 10,000-address list once takes 50,000 credits. The Builder pack ($29.99 for 75,000 credits) covers it with 5,000 verifications to spare; at Scale-pack rates the same list costs $15.00.
Verifying 3,000 signups a month is 15,000 credits a month, or 45,000 over 90 days, so one Builder pack covers the 90 days for which new accounts' credits are valid.
Checking the same address twice is billed twice, even when the second answer comes from a recently stored verdict, so de-duplicate a list before you send it.
Email verification pricing compared
| Provider and plan | Published price | Per 1,000 verifications | Worth knowing |
|---|---|---|---|
| Searlo, Scale pack | $74.99 once for 250,000 credits (50,000 checks) | $1.50 | Every completed check is billed, unknown included |
| Searlo, Micro pack | $3.99 once for 5,000 credits (1,000 checks) | $3.99 | The smallest purchase |
| ZeroBounce, pay as you go | $39 minimum purchase for 2,000 validations | $19.50 | Says it never charges for unknown results; 100 free checks a month with a business domain |
| Hunter, Starter | $49 a month (monthly billing) for 2,000 credits; 0.5 credit per verification | $12.25 if every credit goes to verification | $34 a month billed annually; the plan is an all-in-one outreach platform, not verification alone |
Reading the comparison fairly
Per-1,000 prices are not the whole story. ZeroBounce's pricing page says it never charges for unknown results; Searlo bills every completed check, unknown and accept_all included, and charges nothing only for calls that fail. Hunter sells verification inside a monthly outreach plan, so its per-check cost depends on how you split its credits. Searlo's packs are one-time purchases with no monthly commitment.
ZeroBounce and Hunter are trademarks of their owners; Searlo is independent and not affiliated with either.
Honest limits
Verification lowers the risk of a bounce; it does not remove it. Plan around these:
- No verifier can guarantee delivery. valid means the mail server accepted the mailbox when we checked; the address can still bounce later, be closed, or land in spam.
- Catch-all domains return accept_all, and some large providers accept every recipient during SMTP and bounce afterwards. Neither can be confirmed per mailbox.
- Greylisting and blocked probes produce unknown. retryable: true means a later retry may reach a verdict.
- Every completed check costs 5 credits, whatever the verdict. Calls that fail (5xx) or are rate limited (429) are not charged.
- A repeat check of the same address can be answered from a recently stored verdict (by default up to 15 days for a firm verdict and 1 hour for unknown), and it is still billed.
- One address per call. There is no file upload or list endpoint, and verification is not available through /async/task.
- Default per-account limits are 100 checks a minute, 5,000 an hour and 20,000 a day, so a 10,000-address list takes at least two hours.
- An SMTP conversation is slower than a lookup, and a greylisting server can hold it for seconds. Give each request a client timeout of about 60 seconds.
Where teams verify addresses
- Signup and checkout forms: stop bad and disposable addresses, and suggest didYouMean fixes
- List cleaning before a campaign, to cut hard bounces
- CRM hygiene: re-verify contacts that have gone quiet
- Lead pipelines: verify addresses found during enrichment before any outreach
- Free-trial abuse checks: flag disposable domains at signup
- Routing role addresses such as info@ and sales@ differently from personal inboxes
Related tools and pages
Free email verifier
Check a single address by hand in the browser, with no account.
Data extraction API
Pull email and phone fields from company pages, then verify the addresses here.
Web scraping API
Scrape, crawl and extract public web pages on the same key and credits.
API reference
Endpoints, parameters and responses for every Searlo API.
Credit pack pricing
All six one-time packs and what each costs per 1,000 credits.
Documentation
Authentication, errors and quick starts.
FAQ
Email verification API FAQ
What is an email verification API?
An API that tells you whether an email address can receive mail, without sending it a message. Searlo's checks the syntax, the domain's DNS and MX records and the mailbox itself over SMTP, flags disposable and role addresses, and returns a verdict with the evidence behind it.
How much does it cost to verify 1,000 email addresses?
5,000 credits, because each check is 5 credits. That is $3.99 on the Micro pack, $2.50 on Starter, $2.00 on Builder, $1.50 on Scale, $1.11 on Pro and about $1.00 on Enterprise. The 3,000 free signup credits cover 600 checks.
What is the difference between email validation and email verification?
Validation usually means checking an address's format, and sometimes its domain. Verification goes further and asks the mail server whether the mailbox exists. This API does both in one call: details.format is the validation, and the SMTP check is the verification.
Can I verify emails in bulk?
Yes, by sending one request per address. There is no list-upload endpoint. De-duplicate first, because a repeat check is billed again; pace requests within your limits (by default 100 checks a minute and 20,000 a day); and retry the unknown results marked retryable later rather than straight away.
Does a valid result guarantee delivery?
No, and no verifier can promise that. valid means the mail server accepted the mailbox when we asked. Delivery also depends on your content, your sender reputation and the recipient's filters, and a mailbox can be closed after the check.
What does accept_all mean?
The domain's mail server accepts every address, real or not, so a single mailbox cannot be confirmed. Many companies run catch-all domains. Treat accept_all as possibly fine, and decide by how much a bounce costs you.
Am I charged for unknown results?
Yes. A completed check costs 5 credits whatever the verdict, including unknown and accept_all. Calls that fail with a server error or are rate limited are not charged, and an address that fails the API's basic format validation is rejected with a 400 before any credit is taken.
Is there a free email checker?
Yes: the free email verifier on this site checks single addresses in the browser, with no account. For code, the API's 3,000 free signup credits cover 600 verifications, and no card is needed.
Verify your first 600 addresses free
3,000 free credits on signup, no card. At 5 credits a check, that is 600 verifications.