Day 5 of my 21-day API challenge is done.
Yesterday I built a Text Readability Scorer. Today I built an Email Validator & Disposable Email Checker API — validate any email address and detect fake, throwaway and role-based emails instantly.
Previous days:
- Day 1 — Invoice & Receipt Parser API
- Day 2 — Password Strength & Security Scorer API
- Day 3 — VAT Number Validator API
- Day 4 — Text Readability Scorer API
The Problem
Every app with a signup form has the same problem — fake emails.
Users sign up with throwaway addresses from Mailinator, Guerrilla Mail or one of hundreds of other disposable email providers. They get access to your free tier, abuse it, then disappear. Your marketing metrics get inflated, your email deliverability drops and you waste money sending campaigns to addresses that don't exist.
The fix is simple — check the email before you accept it.
What I Built
The Email Validator & Disposable Email Checker API — send it an email, get back a full analysis.
Input:
{
"email": "test@mailinator.com"
}
Output:
{
"success": true,
"data": {
"email": "test@mailinator.com",
"is_valid": true,
"format_valid": true,
"is_disposable": true,
"is_free_provider": false,
"is_role_based": false,
"is_business": false,
"has_plus_tag": false,
"risk_score": 80,
"risk_level": "high",
"parts": {
"local": "test",
"domain": "mailinator.com",
"tld": "com"
},
"suggestions": [
"This is a disposable email — block it to prevent fake signups"
]
}
}
And for a legitimate business email:
{
"email": "ruan@mycompany.co.za",
"is_valid": true,
"is_disposable": false,
"is_business": true,
"risk_score": 0,
"risk_level": "low"
}
The 6 Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| POST | /validate |
Full email analysis |
| POST | /validate/batch |
Validate up to 50 emails at once |
| POST | /check/disposable |
Quick disposable check only |
| POST | /check/format |
Format validation only |
| POST | /extract |
Extract all emails from a block of text |
The /extract endpoint is my favourite — send it any block of text and it finds and validates every email address in it. Useful for processing documents, contact pages or scraped content.
How Format Validation Works
Email format validation is more complex than most developers realise. The RFC 5321 standard has dozens of rules. Here are the key ones I check:
function validateEmailFormat(email) {
const errors = [];
const trimmed = email.trim().toLowerCase();
// Must have exactly one @
const atCount = (trimmed.match(/@/g) || []).length;
if (atCount === 0) errors.push("Missing @ symbol");
if (atCount > 1) errors.push("Multiple @ symbols found");
const [local, domain] = trimmed.split("@");
// Local part rules
if (local.length > 64) errors.push("Local part exceeds 64 characters");
if (local.startsWith(".")) errors.push("Local part cannot start with a dot");
if (local.endsWith(".")) errors.push("Local part cannot end with a dot");
if (local.includes("..")) errors.push("Cannot contain consecutive dots");
// Domain rules
if (!domain.includes(".")) errors.push("Domain must contain at least one dot");
if (domain.startsWith(".")) errors.push("Domain cannot start with a dot");
// TLD must be letters only, 2-24 chars
const tld = domain.split(".").pop();
if (!/^[a-zA-Z]+$/.test(tld)) errors.push("TLD must contain only letters");
return { valid: errors.length === 0, errors };
}
The API returns specific error messages — not just "invalid email" but exactly what is wrong. That's useful for showing helpful validation messages in your UI.
Disposable Email Detection
The database contains 500+ known disposable email domains. The check is a simple Set lookup — O(1) performance regardless of database size:
const DISPOSABLE_DOMAINS = new Set([
"mailinator.com",
"guerrillamail.com",
"tempmail.com",
"10minutemail.com",
"yopmail.com",
// ... 495 more
]);
const isDisposable = DISPOSABLE_DOMAINS.has(domain);
Using a Set instead of an array means the lookup is instant even with thousands of domains.
Risk Scoring
The API calculates a risk score from 0 to 100:
let riskScore = 0;
if (isDisposable) riskScore += 80; // Major red flag
if (isRoleBased) riskScore += 20; // Minor concern
if (hasPlusTag) riskScore += 10; // Worth noting
riskScore = Math.min(100, riskScore);
| Score | Level | Action |
|---|---|---|
| 0-39 | Low | Accept |
| 40-79 | Medium | Flag for review |
| 80-100 | High | Block |
Role-Based Email Detection
Role-based emails like admin@, support@, info@ and sales@ belong to teams rather than individuals. They are technically valid but often not what you want for personal user accounts:
const ROLE_PREFIXES = new Set([
"admin","administrator","webmaster","support","help","info",
"contact","sales","marketing","billing","noreply","no-reply",
// ... more
]);
const isRoleBased = ROLE_PREFIXES.has(local.split(/[.+_-]/)[0]);
The Batch Endpoint
The /validate/batch endpoint handles up to 50 emails at once and returns a summary:
{
"summary": {
"total": 3,
"valid": 3,
"invalid": 0,
"disposable": 1,
"role_based": 1,
"business": 1,
"free_provider": 1,
"deliverability_rate": 100
}
}
The deliverability_rate tells you what percentage of emails in the list are likely to actually receive mail. Useful for auditing existing email lists before a campaign.
Tech Stack
- Runtime: Node.js + Express
- Hosting: Railway (free tier)
- Marketplace: RapidAPI
- Dependencies: express, cors, helmet, morgan, express-rate-limit
Zero paid APIs. Zero external calls. Pure in-memory validation — fast and cheap to run.
Try It
Live on RapidAPI — search Email Validator Disposable Checker or find me at https://rapidapi.com/user/ruanmul04.
Free tier: 10 requests/month. No credit card required.
What's Next
Day 6 tomorrow — Phone Number Formatter & Validator API.
Every CRM and contact form needs phone validation. I will build format checking, country detection, number type detection and formatting for international numbers.
Follow me here on dev.to to catch every day of the challenge. 🇿🇦
21 APIs in 21 days. Built in South Africa. Sold globally.
Top comments (0)