Article content and detailed guides remain in English. The selected language applies to controls and quick instructions.
Back to articles Email delivery
Real-Time Email Validation on Web Forms: Implementation Guide By Email Extractor Published October 8, 2026 10 min read
email validation web forms real-time validation lead capture data quality
On this page Why Validate Email Addresses at Point of Capture
Every invalid email address that enters your system costs money downstream. Validating at the moment a user types their email on a form prevents bad data from reaching your CRM, email marketing platform or sales team:
Problem
Cost of not validating at capture
Typos (gmial.com, yaho.com)
Lost leads who never receive your emails
Disposable/temporary addresses
Inflated list size; low engagement; wasted sends
Invalid syntax
Bounced emails; damaged sender reputation
Non-existent mailboxes
Hard bounces; potential blacklisting
Role-based addresses (info@, admin@)
Low engagement; some providers penalise senders
Spam traps
Deliverability damage; potential blacklisting
When to validate
Validation timing
What it catches
User experience impact
On blur (field loses focus)
Immediate feedback; catches most issues
Best for single-field forms
On submit
All checks before form submission
Acceptable; user waits briefly at submit
Delayed (async after submit)
Deep checks (SMTP, mailbox verification)
No form delay; follow-up if invalid
On keystroke
Syntax only; too early for domain/mailbox checks
Can feel intrusive; best limited to format hints
Validation Layers
Real-time validation works best as a layered system, from fast/cheap to slow/expensive:
Layer 1: Client-side syntax validation (instant)
Check
What it catches
Implementation
Basic format (contains @, has domain)
Obviously invalid input
HTML5 type="email" or regex
Domain has a dot
Missing TLD (user@gmail)
Regex or string check
No spaces
Accidental spaces
Trim and validate
Reasonable length
Extremely long or short input
Length check (3-254 characters)
No forbidden characters
Characters that are never valid in email
Regex
<!-- HTML5 basic validation -->
<input type="email" required
pattern="[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}"
title="Enter a valid email address">
// Client-side validation function
function validateEmailSyntax(email) {
const trimmed = email.trim();
const errors = [];
if (!trimmed) {
errors.push('Email address is required.');
return { valid: false, errors };
}
if (trimmed.length > 254) {
errors.push('Email address is too long.');
return { valid: false, errors };
}
const parts = trimmed.split('@');
if (parts.length !== 2) {
errors.push('Email must contain exactly one @ symbol.');
return { valid: false, errors };
}
const [local, domain] = parts;
if (!local || local.length > 64) {
errors.push('The part before @ is missing or too long.');
}
if (!domain || !domain.includes('.')) {
errors.push('Domain must include a dot (e.g., gmail.com).');
}
const domainParts = domain.split('.');
const tld = domainParts[domainParts.length - 1];
if (tld && tld.length < 2) {
errors.push('Top-level domain must be at least 2 characters.');
}
return {
valid: errors.length === 0,
errors,
normalised: trimmed.toLowerCase()
};
}
Layer 2: Common typo detection (instant)
Typo pattern
Correction
Method
gmial.com, gmal.com, gamil.com
gmail.com
Domain dictionary + fuzzy matching
yaho.com, yahooo.com
yahoo.com
Domain dictionary + fuzzy matching
hotmal.com, hotmial.com
hotmail.com
Domain dictionary + fuzzy matching
outlok.com, outlool.com
outlook.com
Domain dictionary + fuzzy matching
.con instead of .com
.com
TLD dictionary
.cmo instead of .com
.com
TLD dictionary
// Common domain typo suggestions
const DOMAIN_CORRECTIONS = {
'gmial.com': 'gmail.com',
'gmal.com': 'gmail.com',
'gamil.com': 'gmail.com',
'gmaill.com': 'gmail.com',
'gnail.com': 'gmail.com',
'gmai.com': 'gmail.com',
'yaho.com': 'yahoo.com',
'yahooo.com': 'yahoo.com',
'yhaoo.com': 'yahoo.com',
'hotmal.com': 'hotmail.com',
'hotmial.com': 'hotmail.com',
'hotmai.com': 'hotmail.com',
'outlok.com': 'outlook.com',
'outloo.com': 'outlook.com',
'outlool.com': 'outlook.com',
};
const TLD_CORRECTIONS = {
'con': 'com',
'cmo': 'com',
'ocm': 'com',
'coom': 'com',
'nte': 'net',
'ogr': 'org',
};
function suggestCorrection(email) {
const parts = email.toLowerCase().split('@');
if (parts.length !== 2) return null;
const domain = parts[1];
// Check full domain
if (DOMAIN_CORRECTIONS[domain]) {
return parts[0] + '@' + DOMAIN_CORRECTIONS[domain];
}
// Check TLD
const domainParts = domain.split('.');
const tld = domainParts[domainParts.length - 1];
if (TLD_CORRECTIONS[tld]) {
domainParts[domainParts.length - 1] = TLD_CORRECTIONS[tld];
return parts[0] + '@' + domainParts.join('.');
}
return null;
}
Layer 3: API-based validation (100-500ms)
Check
What it catches
Requires API call
MX record lookup
Domains that cannot receive email
Yes
Disposable email detection
Temporary/throwaway addresses
Yes
Role-based detection
info@, admin@, support@ addresses
Yes (or local list)
SMTP verification
Whether the specific mailbox exists
Yes (slower)
Spam trap detection
Known spam trap addresses
Yes
Catch-all domain detection
Domains that accept all addresses
Yes
// API-based validation (on blur or submit)
async function validateEmailAPI(email) {
try {
const response = await fetch('/api/validate-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const result = await response.json();
return {
valid: result.is_valid,
disposable: result.is_disposable,
role_based: result.is_role_based,
catch_all: result.is_catch_all,
suggestion: result.did_you_mean,
risk: result.risk_level // low, medium, high
};
} catch (error) {
// If API is down, allow the submission
// (validate later in batch)
return { valid: true, api_error: true };
}
}
Server-side validation endpoint
# Flask example: /api/validate-email
import dns.resolver
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
DISPOSABLE_DOMAINS = set() # Load from a maintained list
ROLE_PREFIXES = {
'admin', 'info', 'support', 'sales', 'contact',
'help', 'billing', 'abuse', 'postmaster', 'webmaster',
'noreply', 'no-reply', 'mailer-daemon'
}
def check_mx_records(domain):
"""Check if the domain has MX records."""
try:
mx_records = dns.resolver.resolve(domain, 'MX')
return len(mx_records) > 0
except (dns.resolver.NXDOMAIN,
dns.resolver.NoAnswer,
dns.resolver.NoNameservers):
return False
except Exception:
return True # Assume valid on DNS errors
def is_disposable(domain):
"""Check if the domain is a known disposable email provider."""
return domain.lower() in DISPOSABLE_DOMAINS
def is_role_based(local_part):
"""Check if the local part is a role-based address."""
return local_part.lower().split('+')[0] in ROLE_PREFIXES
@app.route('/api/validate-email', methods=['POST'])
def validate_email():
data = request.get_json()
email = data.get('email', '').strip().lower()
if not email or '@' not in email:
return jsonify({
'is_valid': False,
'reason': 'Invalid format'
})
local, domain = email.rsplit('@', 1)
result = {
'is_valid': True,
'is_disposable': is_disposable(domain),
'is_role_based': is_role_based(local),
'is_catch_all': False,
'risk_level': 'low'
}
# Check MX records
if not check_mx_records(domain):
result['is_valid'] = False
result['reason'] = 'Domain cannot receive email'
result['risk_level'] = 'high'
return jsonify(result)
# Assess risk
if result['is_disposable']:
result['risk_level'] = 'high'
elif result['is_role_based']:
result['risk_level'] = 'medium'
return jsonify(result)
UX Patterns for Real-Time Validation
Showing validation results
Pattern
When to use
Implementation
Inline error below field
Syntax errors, invalid domains
Red text below input field
Suggestion prompt ("Did you mean...?")
Detected typos
Clickable suggestion below field
Green checkmark
Valid email confirmed
Icon inside or beside input field
Yellow warning
Valid but risky (disposable, role-based)
Warning text; allow submission
Loading spinner
API validation in progress
Small spinner inside input field
UX best practices
Practice
Why it matters
Validate on blur, not on every keystroke
Keystroke validation is disruptive; user has not finished typing
Show errors only after the user has finished
Premature errors feel like the form is fighting the user
Make "Did you mean...?" clickable
One click to accept the correction is better than retyping
Allow override for warnings
Some users intentionally use role-based or uncommon addresses
Do not block submission on API timeout
Validation is best-effort; a slow API should not break the form
Show a specific error, not "invalid email"
"This domain cannot receive email" is more helpful than "invalid"
Trim whitespace automatically
Do not show an error for a leading space; just remove it
Make the field wide enough
Narrow fields cause truncation; users cannot see what they typed
Accessibility considerations
Requirement
Implementation
Error messages linked to field
Use aria-describedby to connect error text to the input
Status announcements
Use aria-live="polite" for validation feedback
Colour is not the only indicator
Add text and/or icons alongside colour changes
Keyboard navigation
Suggestion corrections must be keyboard-accessible
Screen reader support
Error and success states must be announced
<div class="form-group">
<label for="email">Email address</label>
<input type="email" id="email" name="email"
aria-describedby="email-feedback"
aria-invalid="false">
<div id="email-feedback" aria-live="polite" role="status">
<!-- Validation feedback appears here -->
</div>
</div>
Validation API Provider Comparison
Provider
Real-time API
Checks included
Pricing (approx.)
Speed
ZeroBounce
Yes
Syntax, MX, SMTP, disposable, catch-all, abuse, spam trap
From $0.008/verification
100-300ms
NeverBounce
Yes
Syntax, DNS, SMTP, disposable
From $0.008/verification
100-500ms
Bouncer
Yes
Syntax, MX, SMTP, disposable, catch-all
From $0.008/verification
100-300ms
Kickbox
Yes
Syntax, MX, SMTP, disposable, role, free provider
From $0.01/verification
100-500ms
Emailable
Yes
Syntax, MX, SMTP, disposable, role, catch-all
From $0.005/verification
100-300ms
Hunter.io
Yes
Syntax, MX, SMTP, disposable
Free tier available; paid from $0.01
200-500ms
Abstract API
Yes
Syntax, MX, SMTP, disposable, catch-all
Free tier available; paid plans
100-300ms
Choosing a provider
Factor
Consideration
Accuracy
Test with known valid, invalid, disposable and catch-all addresses
Speed
Under 300ms for real-time form validation; over 500ms feels slow
Uptime
99.9%+ uptime SLA for production forms
Pricing model
Per-verification vs subscription; watch for overage charges
Free tier
Useful for development and low-volume sites
Integration
SDKs for your language/framework; webhook support
Data coverage
How frequently is the disposable domain list updated?
Privacy
Does the provider store the email addresses you validate?
Integration Patterns
Pattern 1: Client-side only (no API)
Pros
Cons
Free; no API dependency
Cannot verify mailbox existence
Instant feedback
Cannot detect disposable domains (without a bundled list)
Works offline
No SMTP or MX verification
No privacy concerns
Catches only obvious errors
Best for: Low-traffic forms, personal projects, situations where a bounced email is acceptable.
Pattern 2: Client-side + server-side API
Pros
Cons
Comprehensive validation
API cost per validation
Good user experience
API latency (100-500ms)
Catches most invalid addresses
API dependency (needs fallback)
Detects disposable and role-based
Privacy: sending emails to third party
Best for: Business forms, lead capture, signup flows where email quality matters.
Pattern 3: Client-side + delayed batch validation
Pros
Cons
No form delay
Invalid emails enter the system temporarily
Lower API cost (batch pricing)
Requires follow-up process for invalid addresses
Good for high-volume forms
Delay between capture and validation
Allows API failures gracefully
More complex implementation
Best for: High-volume signups, event registrations, situations where form conversion rate is prioritised over immediate validation.
Handling Edge Cases
Edge case
How to handle
Catch-all domains
Accept but flag; cannot verify individual mailbox
New TLDs (.xyz, .io, .dev)
Accept; do not reject based on unfamiliar TLD
Internationalised domains (IDN)
Accept; validate using punycode conversion
Plus addressing (user+tag@domain.com )
Accept; it is valid and commonly used for filtering
Subdomains (user@mail.company.com )
Accept; validate the full domain for MX records
Very long local parts
Accept up to 64 characters (RFC limit)
IP-based domains (user@[192.168.1.1])
Technically valid but unusual; flag for review
Government and military domains (.gov, .mil)
Accept; do not block based on domain type
API timeout
Accept the submission; validate in batch later
Rate limiting from validation API
Queue validations; validate remaining in batch
Measuring Validation Effectiveness
Metric
What to track
Target
Invalid capture rate
Percentage of form submissions with invalid emails before validation
Baseline measurement
Post-validation invalid rate
Percentage of invalid emails that get through validation
Under 2%
False positive rate
Valid emails incorrectly rejected
Under 0.5%
Form abandonment rate
Users who leave the form after validation
Monitor for increases
Typo correction acceptance rate
Users who click "Did you mean...?"
Track to validate suggestion quality
API response time (p95)
95th percentile response time for validation API
Under 500ms
API error rate
Percentage of API calls that fail
Under 1%
Bounce rate (post-validation)
Email bounces from addresses that passed validation
Under 2%
Processing Validated Emails
After collecting validated email addresses through your forms, you may need to consolidate them with emails from other sources. Upload form exports, CRM downloads or other files to Email Extractor to extract and deduplicate email addresses across sources. The tool's case-insensitive deduplication ensures that john@example.com and John@Example.com are treated as one address.