Validate an email address without sending a single message
Your signup form collects an email address. You store it, send a welcome message, and the email
bounces. The address was a typo. Or the domain doesn't exist. Or it was a throwaway
@mailinator.com address that'll be dead in ten minutes. You now have a junk
record in your database, a dinged sender reputation, and a user who'll never come back.
The traditional fix is sending a verification email: "Click this link to confirm your address." It works, but it adds friction. Conversion drops. And it doesn't catch the typo until after you've already sent a message to a bad address.
You can validate an email address before sending anything. Three API calls check syntax, MX records, and disposable domains. The whole process takes under 200ms and catches the problems that cause 90% of email delivery failures.
Three layers of email validation
Each layer catches a different class of bad email address. Use them together for a complete email verification pipeline, or pick the ones you need.
- Syntax validation. Does the address follow a valid format? Does the domain
have a known typo? The
/v1/email/validateendpoint checks RFC 5322 compliance, detects role-based addresses (admin@,support@), and suggests corrections for common domain misspellings likegmial.comorhotmal.com. - MX record verification. Can the domain receive email at all? The
/v1/email-mx/verifyendpoint queries DNS for MX records and falls back to A records. If the domain has no mail server, the address is undeliverable; no amount of retrying will fix it. - Disposable email detection. Is the address from a throwaway service? The
/v1/disposable-email/checkendpoint checks against 700+ known disposable domains and pattern-matches variations. It also flags whether the domain is a free provider (Gmail, Outlook) so you can distinguish between personal and throwaway addresses.
Check email syntax and catch typos
The /v1/email/validate endpoint does the most work in a single call. It validates
format, looks up MX records, checks for disposable domains, and detects typos in popular
provider domains.
curl -X POST https://api.botoi.com/v1/email/validate \
-H "Content-Type: application/json" \
-d '{"email": "jamie@gmial.com"}' Response:
{
"success": true,
"data": {
"email": "jamie@gmial.com",
"is_valid": false,
"format_valid": true,
"mx_found": false,
"mx_records": [],
"is_disposable": false,
"is_free_provider": false,
"is_role_based": false,
"domain": "gmial.com",
"local_part": "jamie",
"suggestion": "jamie@gmail.com"
}
}
The email jamie@gmial.com has valid syntax, but the domain is a typo. The API
returns is_valid: false because no MX records exist for gmial.com,
and it suggests the corrected address jamie@gmail.com. Show this suggestion in
your signup form and you save the user from a dead account.
Verify MX records to check if email is real
The /v1/email-mx/verify endpoint focuses on DNS. It returns the full list of MX
records with priorities and a deliverable field that tells you whether the domain
can receive mail.
curl -X POST https://api.botoi.com/v1/email-mx/verify \
-H "Content-Type: application/json" \
-d '{"email": "orders@acme-corp.com"}' Response:
{
"success": true,
"data": {
"email": "orders@acme-corp.com",
"valid_format": true,
"domain": "acme-corp.com",
"has_mx": true,
"mx_records": [
{ "priority": 1, "exchange": "aspmx.l.google.com" },
{ "priority": 5, "exchange": "alt1.aspmx.l.google.com" }
],
"deliverable": "likely"
}
}
The deliverable field returns one of three values: "likely" (MX
records found), "unknown" (no MX but an A record exists, so the domain might
accept mail), or "unlikely" (no MX and no A records). Use this for MX record
validation when you need the raw DNS data.
Detect disposable and throwaway addresses
The /v1/disposable-email/check endpoint identifies temporary email services.
These addresses work for minutes or hours, then stop accepting mail.
curl -X POST https://api.botoi.com/v1/disposable-email/check \
-H "Content-Type: application/json" \
-d '{"email": "signup-test@mailinator.com"}' Response:
{
"success": true,
"data": {
"email": "signup-test@mailinator.com",
"domain": "mailinator.com",
"is_disposable": true,
"is_free": false,
"provider": "Mailinator"
}
}
The is_free field separates disposable domains from free providers. A Gmail
address returns is_free: true and is_disposable: false. A Mailinator
address returns is_disposable: true and is_free: false. This
distinction matters; blocking all free providers would lock out most of the internet.
Build a validation pipeline in Node.js
Combine all three checks into a single function. This pipeline runs each check sequentially and short-circuits on the first failure.
async function validateEmail(email) {
const headers = { "Content-Type": "application/json" };
const body = JSON.stringify({ email });
// Step 1: Syntax validation + typo detection
const syntaxRes = await fetch("https://api.botoi.com/v1/email/validate", {
method: "POST", headers, body,
});
const syntax = await syntaxRes.json();
if (!syntax.data.format_valid) {
return { valid: false, reason: "Invalid email format" };
}
if (syntax.data.suggestion) {
return {
valid: false,
reason: `Did you mean ${syntax.data.suggestion}?`,
};
}
// Step 2: MX record verification
const mxRes = await fetch("https://api.botoi.com/v1/email-mx/verify", {
method: "POST", headers, body,
});
const mx = await mxRes.json();
if (!mx.data.has_mx) {
return { valid: false, reason: "Domain cannot receive email" };
}
// Step 3: Disposable email detection
const dispRes = await fetch("https://api.botoi.com/v1/disposable-email/check", {
method: "POST", headers, body,
});
const disp = await dispRes.json();
if (disp.data.is_disposable) {
return {
valid: false,
reason: `Disposable email detected (${disp.data.provider})`,
};
}
return { valid: true, reason: null };
}
// Usage
const result = await validateEmail("test@mailinator.com");
console.log(result);
// { valid: false, reason: "Disposable email detected (Mailinator)" } The sequential approach is readable and exits early. If syntax fails, you skip the network calls for MX and disposable checks. If you prefer speed over early termination, run all three in parallel:
async function validateEmail(email) {
const headers = { "Content-Type": "application/json" };
const body = JSON.stringify({ email });
// Run all three checks in parallel
const [syntaxRes, mxRes, dispRes] = await Promise.all([
fetch("https://api.botoi.com/v1/email/validate", {
method: "POST", headers, body,
}),
fetch("https://api.botoi.com/v1/email-mx/verify", {
method: "POST", headers, body,
}),
fetch("https://api.botoi.com/v1/disposable-email/check", {
method: "POST", headers, body,
}),
]);
const [syntax, mx, disp] = await Promise.all([
syntaxRes.json(), mxRes.json(), dispRes.json(),
]);
const errors = [];
if (!syntax.data.format_valid) errors.push("Invalid format");
if (syntax.data.suggestion) errors.push(`Typo: try ${syntax.data.suggestion}`);
if (!mx.data.has_mx) errors.push("No MX records");
if (disp.data.is_disposable) errors.push("Disposable domain");
return {
valid: errors.length === 0,
errors,
details: { syntax: syntax.data, mx: mx.data, disposable: disp.data },
};
}
The parallel version fires all three requests simultaneously using Promise.all.
Total latency equals the slowest request instead of the sum. For most cases, all three
complete in under 100ms.
Python validation pipeline
The same three-step validation in Python. This example uses the requests library
for clarity.
import requests
API_BASE = "https://api.botoi.com/v1"
def validate_email(email: str) -> dict:
headers = {"Content-Type": "application/json"}
payload = {"email": email}
# Step 1: Syntax + typo check
syntax = requests.post(
f"{API_BASE}/email/validate", json=payload, headers=headers
).json()
if not syntax["data"]["format_valid"]:
return {"valid": False, "reason": "Invalid email format"}
suggestion = syntax["data"].get("suggestion")
if suggestion:
return {"valid": False, "reason": f"Did you mean {suggestion}?"}
# Step 2: MX record check
mx = requests.post(
f"{API_BASE}/email-mx/verify", json=payload, headers=headers
).json()
if not mx["data"]["has_mx"]:
return {"valid": False, "reason": "Domain cannot receive email"}
# Step 3: Disposable check
disp = requests.post(
f"{API_BASE}/disposable-email/check", json=payload, headers=headers
).json()
if disp["data"]["is_disposable"]:
provider = disp["data"]["provider"]
return {"valid": False, "reason": f"Disposable email ({provider})"}
return {"valid": True, "reason": None}
# Usage
result = validate_email("jamie@gmial.com")
print(result)
# {'valid': False, 'reason': 'Did you mean jamie@gmail.com?'} When to validate email addresses
Different touchpoints call for different validation depths. Here's where each check fits.
| Touchpoint | Syntax | MX check | Disposable | Why |
|---|---|---|---|---|
| Signup form | Yes | Yes | Yes | Catch typos, dead domains, and throwaway accounts before creating a record |
| Checkout / payment | Yes | Yes | Optional | Receipts need a deliverable address; disposable check depends on your refund policy |
| Contact form | Yes | Optional | No | Basic format check prevents obvious junk; MX check is a bonus |
| Newsletter subscribe | Yes | Yes | Yes | Protects sender reputation; high bounce rates get you flagged by ESPs |
| Lead import / CRM | Yes | Yes | Yes | Cleaning data before it enters your pipeline saves downstream costs |
| Internal tools | Yes | No | No | Syntax check catches data entry errors; internal users are trusted |
Key points
- Validate before storing. Every bad email in your database costs you: storage, failed sends, bounce processing, and sender reputation damage. Catch problems at the point of entry.
- Typo detection saves users. The
/v1/email/validateendpoint catches common domain misspellings and returns a suggestion. Surfacing "Did you mean jamie@gmail.com?" prevents a lost user. - MX checks are fast and free. DNS lookups resolve in milliseconds. There's no reason to skip them. An address with no MX records will never receive your email.
- Disposable detection protects trial abuse. If you offer a free trial or freemium plan, disposable email detection stops the same person from creating unlimited accounts with throwaway addresses.
- No API key needed to start. All three endpoints work anonymously at 5
requests per minute. Test the full pipeline without signing up. Add an
Authorization: Bearer YOUR_KEYheader when you need higher throughput. - Run checks in parallel for speed. The three endpoints are independent.
Using
Promise.allcuts total latency to the slowest single request instead of the sum of all three.
Full documentation for all three endpoints is in the
interactive API docs.
Each endpoint accepts a JSON body with an email field and returns results in
under 100ms from Cloudflare's edge network.
Frequently asked questions
- Can you validate an email address without sending an email?
- Yes. You can check three things without sending anything: whether the address follows valid syntax (RFC 5322), whether the domain has MX records configured to receive mail, and whether the domain belongs to a known disposable email provider. These three checks catch the majority of bad addresses at signup time.
- Is the botoi email validation API free?
- Yes. Anonymous access is available at 5 requests per minute with IP-based rate limiting. No API key, no signup, no credit card required. For higher throughput, paid plans start at $9/mo and include access to all 150+ API endpoints.
- What is the difference between MX record validation and SMTP verification?
- MX record validation checks whether a domain has mail servers configured to accept email. It confirms the domain can receive mail in general. SMTP verification goes further by connecting to the mail server and asking if a specific mailbox exists. MX checks are fast (under 100ms) and free. SMTP checks are slower, can be blocked by mail servers, and may trigger rate limits or spam flags.
- How accurate is disposable email detection?
- The botoi disposable email endpoint checks against 700+ known disposable domains and uses pattern matching to catch variations (domains containing "temp", "trash", "throwaway", etc.). It also distinguishes between disposable domains and free providers like Gmail or Outlook. No detection list is 100% complete, but it catches all widely used throwaway services.
- Should I validate emails on the client or the server?
- Both. Run a basic format check on the client for instant feedback. Run MX and disposable checks on the server before writing to your database. Client-side validation improves the user experience; server-side validation protects your data. Never trust client-side checks alone because they can be bypassed.
Try this API
Email Validation API — interactive playground and code examples
More guide posts
Start building with botoi
150+ API endpoints for lookup, text processing, image generation, and developer utilities. Free tier, no credit card.