Skip to content

Email Validation

Email validation checks whether an address is likely to accept mail before you send to it. Running validation before sending reduces bounces, protects your sender reputation, and saves on sending costs.

The validation pipeline runs 8 checks on each email address:

CheckWhat it does
SyntaxVerifies the email follows RFC 5322 format
MXLooks up MX records to confirm the domain accepts mail
DNSResolves A/AAAA records for the domain
DisposableChecks against 3,000+ known disposable email providers
RoleDetects role-based addresses like admin@, support@, noreply@
TypoSuggests corrections for common domain typos (e.g. gmial.com)
Free providerClassifies the domain as free (Gmail, Yahoo) or corporate
RandomDetects randomly generated local parts using entropy analysis

Each check returns a status: pass, fail, warn, or unknown. The results are combined into a risk score (0.0 to 1.0) and a verdict.

VerdictRisk scoreMeaning
deliverable0.0 – 0.29Safe to send — all checks passed
risky0.3 – 0.69Proceed with caution — some checks flagged issues
undeliverable0.7 – 1.0Do not send — email is likely invalid or dangerous

POST /api/v1/emails/validate

Terminal window
curl -X POST https://api.relaypost.dev/v1/emails/validate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{ "email": "[email protected]" }'
{
"data": {
"email": "[email protected]",
"verdict": "deliverable",
"risk_score": 0.0,
"checks": {
"syntax": { "status": "pass" },
"mx": {
"status": "pass",
"records": [{ "priority": 10, "exchange": "mx.example.com" }]
},
"dns": { "status": "pass", "addresses": ["93.184.216.34"] },
"disposable": { "status": "pass" },
"role": { "status": "pass" },
"typo": { "status": "pass", "suggestions": [] },
"free_provider": { "status": "pass", "provider_type": "unknown" },
"random": { "status": "pass" }
},
"suggestions": [],
"timestamp": "2025-01-15T10:30:00.000Z"
}
}

POST /api/v1/emails/validate/batch

Validate up to 100 addresses in one request. Each result has the same shape as the single validation response.

Terminal window
curl -X POST https://api.relaypost.dev/v1/emails/validate/batch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"emails": [
"invalid@",
]
}'

Instead of validating manually before each send, you can enable auto-validation. When enabled, RelayPost automatically validates every email at send time and rejects addresses that exceed your risk threshold.

GET /api/v1/settings/auto-validation

Terminal window
curl "https://api.relaypost.dev/v1/settings/auto-validation" \
-H "Authorization: Bearer YOUR_API_KEY"

PUT /api/v1/settings/auto-validation

Terminal window
curl -X PUT https://api.relaypost.dev/v1/settings/auto-validation \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{ "threshold": "high" }'
ValueBehavior
offNo automatic validation (default)
highReject only undeliverable emails (risk score > 0.7)
mediumReject undeliverable and risky emails (risk score > 0.3)

Before importing contacts, validate them in batches to remove invalid addresses:

const emails = ["[email protected]", "bob@invalid", "[email protected]"];
const response = await fetch(
"https://api.relaypost.dev/v1/emails/validate/batch",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_API_KEY",
},
body: JSON.stringify({ emails }),
},
);
const { data } = await response.json();
const safe = data.filter((r) => r.verdict === "deliverable");
// Import only the safe addresses

Check email validity during user registration to catch typos early:

const result = await fetch("https://api.relaypost.dev/v1/emails/validate", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_API_KEY",
},
body: JSON.stringify({ email: userInput }),
});
const { data } = await result.json();
if (data.verdict === "undeliverable") {
showError("This email address appears to be invalid");
} else if (data.checks.typo.suggestions.length > 0) {
const suggestion = data.checks.typo.suggestions[0].suggested;
showSuggestion(`Did you mean ${suggestion}?`);
}