Startups burn through $40,000+ in email marketing costs sending campaigns to addresses that don't exist. It happens more often than you'd think.
Not spam. Not unsubscribes. Just... bounces. Thousands of them. Sender reputation tanks so hard that eventually emails to real customers start landing in spam folders. The whole domain gets flagged.
All because their signup form accepted anything that looked vaguely email-shaped.
This happens more than you'd think. And it's completely preventable.
The Regex Trap
Every developer's first instinct is regex. You google "email validation regex," you find something like this on Stack Overflow:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(email)) {
// "valid" email
}
Here's what this catches: whether there's an @ symbol with stuff on both sides and a dot somewhere after it.
Here's what it doesn't catch:
-
test@doesnotexist.invalid— Perfect format. Domain doesn't exist. -
nobody@gmail.con— That's not gmail.com. Typo. -
temp@10minutemail.com— Real email. Abandoned in 10 minutes. -
support@myspace.com— Valid domain. Mailbox might not exist.
The regex validates format. It doesn't validate reality.
I've seen production systems running the "official" RFC 5322 email regex—all 6,300 characters of it—and still letting through garbage because technical validity isn't the same as practical validity.
What Actually Matters
When someone gives you an email address, you need to know three things:
1. Can mail be delivered there?
Does the domain exist? Does it have MX records (mail server configuration)? Can you actually connect to that mail server? These are real network checks, not pattern matching.
2. Is the mailbox real?
Some mail servers will tell you if a specific address exists. Not all do—Gmail won't, for privacy reasons—but many corporate mail servers will reject invalid addresses at the SMTP level.
3. Is it a throwaway?
Guerrilla Mail. 10 Minute Mail. Mailinator. Thousands of services exist specifically to give people disposable addresses. If someone's using one, they're probably not a real lead.
None of this is possible with regex.
How Real Validation Works
const response = await fetch(
`https://api.apiverve.com/v1/emailvalidator?email=${encodeURIComponent(email)}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { data } = await response.json();
One call. Here's what comes back:
{
"status": "ok",
"data": {
"email": "john.doe@acme-corp.com",
"domain": "acme-corp.com",
"username": "john.doe",
"isValid": true,
"isRegexValid": true,
"isMxValid": true,
"isSmtpValid": true,
"canConnect": true,
"hasTypo": false,
"isCompanyEmail": true,
"isFreeEmail": false,
"smtp": {
"valid": true,
"reason": "Connected"
}
}
}
Let's break down what each of these tells you.
isRegexValid
Yes, the API does the regex check too. You get it for free. But this is just the first gate, not the final answer.
isMxValid
Does this domain have MX records? MX records tell the internet where to deliver mail for a domain. No MX records = domain doesn't receive email = fake address.
This catches all those @doesnotexist.invalid addresses instantly.
isSmtpValid and canConnect
Can we actually reach the mail server and talk to it? Some domains have MX records pointing to servers that don't respond. Maybe they're misconfigured. Maybe they went out of business. Either way, you can't deliver mail there.
hasTypo
This is underrated. The API knows common email domains and catches typos:
-
gmial.com→ probably meantgmail.com -
yaho.com→ probably meantyahoo.com -
hotmal.com→ probably meanthotmail.com
Instead of bouncing their first email and losing the user forever, you can prompt: "Did you mean @gmail.com?"
This single field probably saves more signups than everything else combined.
isCompanyEmail vs isFreeEmail
Here's where it gets interesting for B2B.
If someone signs up with jane@bigcorp.com, they're probably from BigCorp. That's a sales lead.
If someone signs up with randomuser847@gmail.com, they're... someone with a Gmail account. Could be a tire-kicker. Could be a student. Could be the CEO of a company who just prefers Gmail.
You shouldn't reject free email addresses. But you might want to:
- Route company emails to sales immediately
- Ask free email users for more context
- Prioritize company emails in your outreach
This field lets you segment automatically.
The Disposable Email Problem
Valid emails aren't all created equal.
const dispResponse = await fetch(
`https://api.apiverve.com/v1/emaildisposablechecker?email=${encodeURIComponent(email)}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { data } = await dispResponse.json();
if (data.isDisposable) {
// This address will be abandoned in minutes
}
Disposable email services are popular because they work. Need to download a whitepaper without getting spammed? 10minutemail.com. Signing up for a free trial you'll never use? guerrillamail.com.
The Disposable Email Checker API knows about thousands of these services—and the list grows constantly because new ones pop up all the time.
Should you block them? Depends on your business.
Block if: You're collecting leads for sales, offering limited free trials, or building anything where fake signups hurt you (abuse, fraud, gaming free tiers).
Allow with caution if: You just need a working email for transactional messages. The address might be temporary, but it works right now.
Don't block if: You're running a privacy-focused service where you actually want to let people stay anonymous.
Most B2B SaaS should probably block or at least flag disposable addresses. Your "10,000 free users" aren't impressive if 8,000 of them are the same person rotating through temporary emails.
Putting It All Together
Here's a complete validation function that handles everything:
async function validateEmail(email) {
const headers = { 'x-api-key': process.env.APIVERVE_KEY };
// Step 1: Validate the email itself
const validRes = await fetch(
`https://api.apiverve.com/v1/emailvalidator?email=${encodeURIComponent(email)}`,
{ headers }
);
const { data: validity } = await validRes.json();
// Hard failures
if (!validity.isValid || !validity.isMxValid) {
return {
valid: false,
reason: 'invalid',
message: "That email address doesn't look right. Double-check it?"
};
}
// Typo detection
if (validity.hasTypo) {
// In a real app, you'd suggest the correction
// The domain tells you what they probably meant
return {
valid: false,
reason: 'typo',
message: `Did you mean @${validity.domain.replace(/gmial|gmai(?!l)|yaho(?!o)|hotmal(?!l)/, match => {
const fixes = { 'gmial': 'gmail', 'gmai': 'gmail', 'yaho': 'yahoo', 'hotmal': 'hotmail' };
return fixes[match] || match;
})}?`,
originalDomain: validity.domain
};
}
// Step 2: Check for disposable addresses
const dispRes = await fetch(
`https://api.apiverve.com/v1/emaildisposablechecker?email=${encodeURIComponent(email)}`,
{ headers }
);
const { data: disposable } = await dispRes.json();
if (disposable.isDisposable) {
return {
valid: false,
reason: 'disposable',
message: "Please use a permanent email address."
};
}
// Everything checks out
return {
valid: true,
isCompanyEmail: validity.isCompanyEmail,
isFreeEmail: validity.isFreeEmail,
domain: validity.domain,
deliverable: validity.isSmtpValid
};
}
Now your signup form can give actually helpful feedback:
const result = await validateEmail(userInput);
if (!result.valid) {
showError(result.message);
return;
}
if (result.isCompanyEmail) {
// Flag for sales team
await tagLead(email, 'company-email');
}
// Continue with signup
When to Validate
Don't validate on every keystroke. That's annoying and expensive.
Validate on blur (when they leave the field). They've finished typing, now check it.
Or validate on submit. The whole form at once.
Client-side first, always. Before burning an API credit, do a basic sanity check:
emailInput.addEventListener('blur', async (e) => {
const email = e.target.value.trim();
// Quick local checks first
if (!email) return;
if (!email.includes('@')) {
showError("That doesn't look like an email address");
return;
}
// Now the real validation
setLoading(true);
const result = await validateEmail(email);
setLoading(false);
if (!result.valid) {
showError(result.message);
} else {
clearError();
}
});
This way you're not burning credits validating empty strings or asdf.
The Business Impact
Let me get concrete about why this matters.
Deliverability Math
Email deliverability is a numbers game. Mailbox providers (Gmail, Outlook, Yahoo) track your bounce rate. Too many bounces? You get flagged.
Industry benchmarks say keep your bounce rate under 2%. That means if you send 1,000 emails, fewer than 20 should bounce.
Without validation, I've seen bounce rates hit 15-20% on cold outreach. That's not just wasted emails—it's actively destroying your sender reputation.
The Cost of Bad Data
Let's say you're paying $0.001 per email sent (pretty standard for transactional email at scale).
Send 100,000 emails with a 15% invalid rate = 15,000 bounces = $15 wasted directly, plus:
- Damage to sender reputation (unmeasurable but significant)
- Lost legitimate emails that land in spam
- Time debugging why engagement dropped
Validating those 100,000 emails upfront at 1 credit each costs... let's call it $10 on APIVerve's pricing. You're paying $10 to save $15+ and protect your reputation.
The math gets more dramatic at scale.
Conversion Rates
This is the hidden cost nobody talks about.
User signs up with john@gmial.com. You send them a welcome email. It bounces. They never come back because they think you ghosted them.
That's not a bad email address. That's a typo. The user wanted to sign up. The hasTypo check catches this and lets you prompt for correction instead of losing the customer.
I don't have exact numbers—nobody does—but fixing typos at signup almost certainly moves your conversion rate by meaningful percentage points.
Caching Validation Results
You don't need to re-validate the same email every time.
const emailCache = new Map();
async function validateEmailCached(email) {
const normalized = email.toLowerCase().trim();
if (emailCache.has(normalized)) {
const cached = emailCache.get(normalized);
if (Date.now() < cached.expiresAt) {
return cached.result;
}
}
const result = await validateEmail(normalized);
// Cache for 24 hours
emailCache.set(normalized, {
result,
expiresAt: Date.now() + (24 * 60 * 60 * 1000)
});
return result;
}
Email validity doesn't change every second. Caching for a day is fine. Caching for a week is probably fine. An email that was valid yesterday is almost certainly still valid today.
When Not to Block
A few situations where you might want to accept questionable emails anyway:
Password reset flows. If someone's trying to reset their password, you want them to get the email even if it looks sketchy. Don't lock people out of their accounts because their email domain is weird.
Transactional receipts. They bought something, they get a receipt. Even if it bounces, you tried.
Account recovery. Similar to password reset. Be lenient when users are already frustrated.
Validation is for new data coming in. Existing users who've proven they receive email shouldn't be re-challenged.
What I'd Actually Build
If I were starting a SaaS today, here's my email validation setup:
- Basic regex on the frontend to catch obvious garbage
- Email Validator API call on blur or submit
- Typo correction — prompt users to fix instead of rejecting
- Disposable Email Checker to block throwaway signups
- isCompanyEmail flag piped to CRM for sales prioritization
- 24-hour caching of validation results
- Monitoring of bounce rates to catch issues early
That's maybe 50 lines of code and completely transforms your data quality.
Email validation isn't glamorous. Nobody brags about it at conferences. But it's one of those quiet infrastructure decisions that pays dividends forever.
The Email Validator API and Disposable Email Checker are both available on all APIVerve plans, including free. You can have this running in your app today.
Stop letting fake emails through. Your sender reputation will thank you.
Originally published at APIVerve Blog
Top comments (0)