Blocking disposable email domains is the easy half
We keep a list of throwaway email domains. It has 162,587 entries in it right now, refreshed hourly from several public sources plus our own reports, and it is the least interesting thing we do with email.
The list catches someone who could not be bothered. The signup that actually costs you money uses a real address at a real provider, and it does it six times.
What the list is genuinely good for#
It is worth being precise about what a disposable-domain check buys you, because it does buy something. Run four addresses through the checker and the difference is stark:
| address | disposable | on an abuse list | valid MX | score |
|---|---|---|---|---|
a@guerrillamail.com | yes | yes | yes | 70 |
b@mailinator.com | yes | yes | yes | 95 |
c@10minutemail.com | yes | no | no | 80 |
d@proton.me | no | no | yes | 0 |
That is a real filter and it costs one lookup. If you take nothing else from this, take the list: it removes a meaningful slice of low-effort abuse before it reaches your funnel.
The heuristic everyone reaches for, which does not work#
Ask most people how they would catch a throwaway domain without a list and they will suggest domain age. New domain, high risk. It sounds obviously right.
Here is the actual registration age of the three throwaway domains above:
| domain | age |
|---|---|
mailinator.com | 23.1 years |
guerrillamail.com | 19.7 years |
10minutemail.com | 19.7 years |
These are some of the oldest domains your signup form will ever see. They predate most legitimate SaaS companies. A domain-age rule does not merely miss them, it actively vouches for them, and it will penalise the genuinely new startup whose employee is trying to sign up.
Domain age is a useful signal for other things. It is close to useless for this one.
The half that actually matters#
The interesting problem is not "is this domain disposable." It is "have I seen this person before, wearing a different address."
Every major provider hands users several ways to write the same mailbox. Gmail ignores dots
entirely. Anything after a + is a label, not an address. googlemail.com is the same service as
gmail.com. Case never mattered. Stack those and one inbox produces effectively unlimited unique
looking addresses, all of which pass every validity check you can throw at them, because they are
all real.
ada.lovelace@gmail.comdotsa.d.a.lovelace@gmail.comdotsadalovelace+shop@gmail.complus tagadalovelace+shop2@googlemail.complus + domainAdaLovelace@Gmail.comcaseadalovelace@gmail.comthe actual inbox
adalovelace@gmail.comSix accounts that looked unrelated are one person.
None of those six is disposable. None is malformed. All of them deliver. Every one would sail through a signup form, and to a database keyed on the raw string they are six different customers claiming six welcome bonuses.
The check that matters reduces the address to its canonical form before it ever looks at the domain:
ada.lovelace@gmail.com → adalovelace@gmail.com a.d.a.lovelace@gmail.com → adalovelace@gmail.com adalovelace+shop@gmail.com → adalovelace@gmail.com adalovelace+shop2@googlemail.com → adalovelace@gmail.com AdaLovelace@Gmail.com → adalovelace@gmail.com
One identity key. Six accounts that looked unrelated are one person, and you can see it at signup rather than at payout.
The rules are provider-specific, which is why doing it properly is more work than it looks. Dots are meaningless at Gmail and meaningful nearly everywhere else. Strip them globally and you will merge two genuinely different people at a provider that treats them as distinct, which is a worse failure than missing a duplicate.
Doing it without building it#
Every one of the checks above runs on a single lookup, and there is a standalone email endpoint so you do not have to spend a scoring event to see one:
curl -s -X POST https://api.kaidn.io/v1/check/email \ -H "x-api-key: $KAIDN_API_KEY" \ -H "content-type: application/json" \ -d '{"email":"a.d.a.lovelace+shop@googlemail.com"}'
{
"email": {
"fraud_score": 12,
"canonical": "adalovelace@gmail.com",
"is_aliased": true,
"alias_tricks": ["dot_trick", "plus_tag", "domain_alias"],
"has_plus_tag": true,
"is_disposable": false,
"mx_valid": true,
"catch_all": false,
"looks_gibberish": false,
"is_malformed": false
},
"reputation": { "recent_abuse": false, "network_risk": 0 },
"summary": "Real Gmail mailbox reached through three aliasing tricks. Not disposable."
}canonical is the field to key your accounts on. Everything else is evidence about the domain;
that one line is the identity.
On a scored event the same work shows up as two separate checks, which is the point of the section
below: emailRisk reports what the address IS, and emailReuse reports how many accounts the
canonical mailbox is already behind.
const r = await kaidn.score({ event: "signup", user_id, ip, email, device_id }); // "This mailbox is behind 4 accounts" is the finding. The aliasing is how it hid. const reuse = r.checks.find((c) => c.check === "emailReuse"); if (reuse) console.log(reuse.message, reuse.evidence);
What else the inbox tells you#
Beyond disposability and aliasing, an address carries infrastructure that is much harder to fake than the address itself:
- MX records. A domain with none cannot receive mail.
10minutemail.comabove has no valid MX and still accepts signups everywhere. - SPF and DMARC. Whether the domain publishes sender authentication at all.
- Catch-all. A domain that accepts every address means the local part tells you nothing, so
anything@theirdomain.comis infinite free addresses. - Mail host. Where the MX actually points. A "company" domain whose mail is served from a VPS the same operator rents is a different proposition to one on Google Workspace.
- Role accounts.
info@,support@,admin@. Not a person. - Gibberish. Machine-generated local parts, scored on the canonical form so aliasing cannot hide them.
The mistake we made ourselves#
Here is one worth admitting, because it is easy to get wrong in the same direction.
We used to score every one of those signals independently and add them up. That seems obviously correct until you notice that a throwaway domain trips several of them at once: it is on the disposable list and on an abuse list and frequently has no MX. One fact, counted three times, and the score inflates accordingly.
Worse, it inflated in the wrong direction on legitimate users too. A real customer who signs up
with adalovelace+shop@gmail.com was scored for plus-addressing and for aliasing. Two
penalties for one entirely reasonable habit that plenty of careful people have.
Correlated signals now collapse: within a cluster only the strongest scores, while every detection is still reported as evidence. Our labelled fixtures did not move at all on the fraud cases. The only thing that changed was that legitimate plus-addressed user, whose score dropped by 40%.
If you are building scoring of your own, that is the trap. Adding more signals feels like adding more accuracy. If the signals are correlated you are mostly adding confidence to a number that has not learned anything new.
What to actually do#
If you are starting from nothing, in order of return on effort:
- Canonicalize before you store. Even if you do nothing else, key your accounts on the canonical address. This is the one that catches repeat signups.
- Check the disposable list. Cheap, and it clears out the low-effort attempts.
- Check MX. A domain that cannot receive mail is not a customer.
- Do not use domain age for this. It fails on the exact cases you care about.
- Watch for correlation in whatever you score, so one fact does not become three.
You can run any address through the free email checker with no account. It returns the same report the API does, canonical form included.
Frequently asked questions
Should I block disposable email addresses outright?
Usually not. Block on evidence rather than on a single flag. Throwaway domains do correlate with abuse, but a score that combines the inbox with the device, the IP and the account history will be right more often than a hard rule on one field, and a hard rule costs you the legitimate users who happen to use a privacy provider.
Why not just block plus-addressing?
Because plenty of legitimate, privacy-conscious people use it deliberately, and blocking them costs you real customers. Collapse aliases to one canonical identity so you can count them, then decide based on how many accounts that single inbox has actually opened. Counting is the useful operation; refusing is not.
Does domain age work as a disposable-email signal?
No, and it fails in the worst possible direction. mailinator.com is 23.1 years old, guerrillamail.com and 10minutemail.com are both 19.7 years old. These are among the oldest domains a signup form will ever see, so a domain-age rule does not merely miss them, it actively vouches for them while penalising the genuinely new startup whose employee is trying to sign up.
Does email checking work without a device fingerprint?
It helps on its own, but the inbox is one entity among several. In practice the strongest duplicate-account signal is usually the device, and the inbox is what confirms it. Two accounts sharing a device is ambiguous (a household, an office); two accounts sharing a canonical mailbox is not, because the person who receives the mail is the same person.
Why is canonicalisation provider-specific?
Because the rules genuinely differ. Dots are meaningless at Gmail and meaningful nearly everywhere else, so stripping them globally merges two different people at a provider that treats them as distinct. That is a worse failure than missing a duplicate, which is why doing this properly is more work than the one-line regex it looks like.
How large is the disposable-domain list, and how often does it change?
162,587 entries at the time of writing, refreshed hourly from several public sources plus first-party reports. The number matters less than the refresh: public lists lag new domains by weeks, which is precisely why the list is the easy half and identity resolution is the half that catches anybody who is trying.
Score your own traffic
10,000 events a month on the free tier, no card. One POST to /v1/score and you get a verdict with the evidence behind it.