How to Detect Disposable Emails with an API
Static blocklists and regex patterns miss most disposable emails. Learn how to detect burner addresses reliably using API-based validation, with Node.js code examples and a step-by-step integration guide.
The Disposable Email Problem Is Bigger Than You Think
Somebody signs up for your app. The email looks normal. They poke around, maybe burn through some free credits, and vanish. A week later you notice the domain was sharklasers.com. Or guerrillamail.de. Or one of the roughly 5,000 other disposable email providers that exist right now.
Disposable emails are not some niche trick used by a handful of privacy enthusiasts. They are an industry. Tempmail.io alone serves over 40 million temporary inboxes per month. Guerrilla Mail has been running since 2006. New providers pop up weekly, and some of them are purpose-built for abuse: sign up, verify, extract value, disappear.
If you run a SaaS product with a free tier, this matters a lot. Free-tier abuse can cost thousands per month in wasted compute, poisoned metrics, and support overhead. And disposable emails are the front door for most of it.
So how do you actually detect them? There are three common approaches, each with very different tradeoffs.
Approach 1: Regex Patterns
The first instinct most developers have is to write some regex. Maybe block emails that look "too random" or match known burner domain patterns.
// The naive regex approach
function isDisposableEmail(email: string): boolean {
const localPart = email.split('@')[0];
// Block obvious random strings
const looksRandom = /^[a-z0-9]{15,}$/i.test(localPart);
// Block some known patterns
const knownPatterns = /^(test|temp|fake|spam|trash)d*@/i.test(email);
return looksRandom || knownPatterns;
}This catches maybe 5% of disposable emails. Generous estimate.
The problem is fundamental: you are trying to identify a service by looking at the user's input, not the service itself. Someone using Tempmail might get an address like margaret.chen@tempmail.io. Nothing random about it. Nothing in the local part screams "disposable." The randomness heuristic also produces false positives constantly. Real people have emails like jx2847@alumni.university.edu because their school assigned it to them in 2009.
Regex is the wrong tool for this job. It is pattern matching where you need intelligence about the domain.
Approach 2: Static Domain Lists
The next step up is maintaining a blocklist of known disposable email domains. This is what most open-source solutions do, and it is a genuine improvement over regex.
// Static blocklist approach
const DISPOSABLE_DOMAINS = new Set([
'tempmail.io',
'guerrillamail.com',
'guerrillamail.de',
'sharklasers.com',
'grr.la',
'mailinator.com',
'yopmail.com',
'throwaway.email',
'temp-mail.org',
'fakeinbox.com',
// ... hundreds more
]);
function isDisposableEmail(email: string): boolean {
const domain = email.split('@')[1].toLowerCase();
return DISPOSABLE_DOMAINS.has(domain);
}
// Usage
isDisposableEmail('user@tempmail.io'); // true
isDisposableEmail('user@gmail.com'); // false
isDisposableEmail('user@newburner.xyz'); // false <-- missed!This works reasonably well for the domains you know about. The popular open-source lists on GitHub contain anywhere from 3,000 to 8,000 domains. That sounds like a lot until you realize the actual number is north of 5,000 active providers and growing.
Why static lists fall behind
Three reasons static lists fail in practice:
- New domains appear constantly. Disposable email services register new domains specifically to evade blocklists. Some rotate through fresh domains weekly. By the time a domain makes it onto a community-maintained list, it has already been used for thousands of signups.
- Subdomain tricks. Some services let users create addresses on subdomains like
anything.tempmail.io. Your blocklist hastempmail.iobut not the infinite subdomain variations. You need wildcard matching, which the simple Set lookup above does not handle. - Catch-all domains masquerading as real providers. Not all disposable email services look like disposable email services. Some use professional-sounding domains. Some are custom domains with catch-all MX records pointed at a disposable inbox service. A static list cannot catch what it does not know about.
Actually, that is not quite right. There is a fourth reason that might be the most important: maintenance burden. Someone on your team has to keep the list updated. They have to monitor for new providers, merge upstream changes from open-source repos, and handle edge cases when a domain turns out to be a legitimate service. In practice, nobody does this consistently. The list gets stale within months.
Approach 3: API-based detection
An API-based approach offloads the detection logic to a service that specializes in it. Instead of maintaining your own lists and heuristics, you send the email to an endpoint and get back a risk assessment.
Here is what that looks like with the BigShield SDK:
import { BigShield } from 'bigshield';
const shield = new BigShield(process.env.BIGSHIELD_API_KEY);
async function checkEmail(email: string) {
const result = await shield.validate(email);
return {
riskScore: result.risk_score, // 0-100
riskLevel: result.risk_level, // very_low to very_high
signals: result.signals, // detailed signal breakdown
recommendation: result.recommendation, // accept, review, or reject
fraudDecision: result.fraud_decision, // allow, review, require_verification, or block
};
}
// Example usage
const check = await checkEmail('user@guerrillamail.com');
// {
// riskScore: 12,
// riskLevel: "very_high",
// signals: [
// { name: "burner-detection", score_impact: -35, confidence: 1.0, ... },
// { name: "domain-reputation", score_impact: -15, confidence: 0.95, ... },
// { name: "domain-age", score_impact: -8, confidence: 0.8, ... },
// ...
// ],
// recommendation: "reject",
// fraudDecision: "block"
// }The difference is not just convenience. An API can run checks that are impossible with a static list.
What signals go beyond domain matching
Checking an email against a domain blocklist is one signal. A good validation API runs dozens of signals simultaneously. Here is what happens behind the scenes when you validate an email through BigShield:
DNS and MX record analysis
Every email domain needs MX (Mail Exchange) records to receive mail. Disposable services often have unusual MX configurations. Some point to shared infrastructure that handles hundreds of disposable domains. Some have no MX records at all, relying on web-only access. Checking MX records tells you whether a domain is set up like a real email provider or a throwaway service.
SMTP verification
You can actually ask a mail server whether a specific address exists without sending an email. This is SMTP verification. It catches a different class of problems: typos in real addresses, deactivated accounts, and domains that accept all addresses (catch-all servers, which are common in disposable services).
Domain age and registration patterns
A domain registered three days ago that is already receiving signups for your app is suspicious. Legitimate email providers have domain histories measured in years. Disposable services frequently rotate through freshly registered domains. Checking domain age and registration patterns catches new burner domains that have not made it onto any blocklist yet.
Local part pattern analysis
Even when the domain is legitimate (like gmail.com), the local part can reveal a lot. We wrote about this in detail before, but the short version: algorithmically generated email addresses have measurably different character distributions, entropy levels, and structural patterns compared to addresses chosen by humans. A string like kx9vm2qr7p before the @ sign is not proof of fraud, but it is a meaningful signal when combined with other checks.
Historical abuse data
When a validation service processes millions of emails, it builds a picture of which domains and patterns are associated with abuse. This is data you simply cannot replicate with a static list. If a brand-new disposable domain is used in 200 signups across different BigShield customers in a single day, the system learns about it in real time.
Practical comparison
| Regex | Static List | API | |
|---|---|---|---|
| Detection rate | ~5% | ~60-70% | ~95%+ |
| False positives | High | Low | Very low |
| Catches new domains | No | No | Yes |
| Maintenance cost | Low | Medium-High | None |
| Latency | <1ms | <1ms | ~100ms |
| Signals checked | 1 | 1 | 30+ |
| Implementation time | 30 min | 2-4 hours | 15 min |
| Cost | Free | Free | Free tier available |
The latency tradeoff is real. Regex and static lists are essentially instant. An API call adds around 100ms to your signup flow. For most applications, that is completely invisible to the user, especially if you run the validation asynchronously while they fill in the rest of the form. But if you are building something latency-critical, it is worth considering.
Integrating into a signup flow
Here is a practical example of how you would wire this into a Next.js API route handling user registration:
import { BigShield } from 'bigshield';
import { NextRequest, NextResponse } from 'next/server';
const shield = new BigShield(process.env.BIGSHIELD_API_KEY);
export async function POST(req: NextRequest) {
const { email, name, password } = await req.json();
// Step 1: Validate the email
const result = await shield.validate(email, {
ip: req.headers.get('x-forwarded-for') ?? undefined,
});
// Step 2: Decide what to do based on the result
if (result.recommendation === 'reject') {
// Score below 50: almost certainly fake or disposable
return NextResponse.json(
{ error: 'Please use a valid email address.' },
{ status: 422 }
);
}
if (result.recommendation === 'review') {
// Score between 50-74: uncertain, flag for review
// Still create the account but limit access
const user = await createUser({ email, name, password, flagged: true });
return NextResponse.json({ user, status: 'pending_review' });
}
// Score 75+: looks legitimate
const user = await createUser({ email, name, password, flagged: false });
return NextResponse.json({ user, status: 'active' });
}A few things worth noting about this pattern:
- Never tell the user WHY their email was rejected. A generic "Please use a valid email address" is intentional. If you say "Disposable emails are not allowed," you are teaching abusers exactly what to try next.
- The middle tier matters. Not every suspicious email is fake. A score of 55 might be a real person using a slightly unusual email setup. Flagging for review instead of blocking outright protects you from false positives.
- Run validation server-side. Client-side validation can be bypassed in about 30 seconds with browser dev tools. The API call must happen on your server.
Handling edge cases
Real-world integration always has edge cases. Here are the ones that come up most often:
Corporate catch-all domains
Some companies configure their email servers to accept mail for any address at their domain. This looks similar to disposable services with catch-all configurations. A good validation API distinguishes between a Fortune 500 company's catch-all server and a burner domain's catch-all. Domain age, MX record reputation, and historical data make this possible.
Privacy-focused email providers
Services like ProtonMail and Tutanota are not disposable. They are legitimate privacy-focused providers. But some validation approaches incorrectly flag them because they share characteristics with burner services. Make sure whatever solution you use handles these correctly. Blocking ProtonMail users is a good way to alienate your most security-conscious customers.
Plus addressing and dots
Gmail treats john.doe@gmail.com and johndoe@gmail.com as the same address. It also ignores everything after a + sign, so johndoe+signup@gmail.com goes to the same inbox. This is not disposable email usage. Many legitimate users use plus addressing to organize their inbox. Do not block it. But do normalize it when checking for duplicate accounts.
Beyond HTTP: AI agents and MCP
If you are building AI agents that need to validate emails as part of their workflow, hitting a REST API works fine. But there is a more native option. BigShield provides an MCP (Model Context Protocol) server that lets AI agents call email validation as a tool directly. No HTTP client setup, no response parsing. The agent just calls the validation tool and gets structured results. Worth looking into if you are building agent-based systems.
What this costs in practice
The obvious question: is API-based validation worth paying for?
Consider the math. A SaaS app with 5,000 signups per month and a 25% disposable email rate is letting through about 1,250 fake accounts monthly. If each fake account costs you even $2 in compute, support, and data pollution, that is $2,500 per month in waste.
BigShield's free tier covers 1,500 validations per month, which handles the example above without spending anything. If you need more volume, paid plans start well below what most companies lose to a single month of unchecked disposable email abuse.
The real cost of not validating is not the fraud you can measure. It is the compounding damage to your metrics, your infrastructure costs, and the engineering time you spend cleaning up problems that should have been prevented at the door.
Getting started
If you want to try this out, here is the fastest path:
- Create a free account at bigshield.app (no credit card required)
- Grab your API key from the dashboard
- Install the SDK:
npm install bigshield - Add the validation check to your signup endpoint using the pattern above
The free tier gives you 1,500 validations per month. That is enough to protect a small-to-medium signup flow and see the results for yourself before committing to anything. Run it alongside your existing flow for a week, compare what it catches versus what you are catching now, and decide from there.