Find fake and duplicate accounts in your existing user list
You added an email check to your signup form. New accounts are clean from today. But the 20,000 users who signed up before that still include throwaway inboxes, addresses that bounce, and one person with eleven Gmail aliases sitting on eleven free tiers.
This guide covers how to find them, and, more importantly, what to do with each kind without emailing, blocking or merging the wrong people.
Why bother with old accounts
- Bounces hurt your sender reputation. Every product email, invoice and newsletter sent to a dead address counts against you with Gmail and Outlook. Enough bounces and your real users' emails start landing in spam.
- Your numbers are wrong. "Active users", conversion rates and churn all include accounts that were never real.
- Abusers keep what they took. Duplicate accounts still hold their free credits and referral bonuses.
- Your new duplicate check has gaps. If you just added a canonical email column, old users don't have a value in it yet, so an alias of an old account can still sign up.
The four things to look for
| Problem | Example | What it means |
|---|---|---|
| Disposable address | abc@yopmail.com | Public throwaway inbox; nobody will read your email |
| Undeliverable address | maria@gmaill.con, _x@gmail.com | Can never receive mail: typo or made up |
| Alias duplicate | john+2@gmail.com and j.o.h.n@gmail.com | Same inbox, several accounts |
| Privacy relay | quiet-fox@duck.com | Real inbox, but unlinkable to the owner's other ones |
Relay addresses are not a problem in themselves (see Hide My Email and other relays at signup). Flag them only if they hold valuable free perks.
Step 1: export your users
You need one row per account with at least the email. Keep the account id so you can join the results back:
\copy (SELECT id, email, created_at FROM users ORDER BY id) TO 'users.csv' WITH CSV HEADER
In MySQL use SELECT ... INTO OUTFILE, or export from your admin panel. Any CSV with an email column works.
Step 2: check every address
You can do parts of this yourself: lowercase and normalize Gmail addresses with a script, and look up MX records for each domain. The hard parts are the per-provider alias rules (dots matter at Outlook but not at Gmail) and a disposable list that is current. Get the alias rules wrong and you "merge" two strangers.
With Signitize, one command checks the whole file on your machine (bulk checks are part of Premium):
SIGNITIZE_API_KEY=sk_live_... npx signitize clean users.csv
Rows in file: 18,250
Checked now: 18,212
Disposable: 1,904
Undeliverable: 311
Privacy relay: 97
Duplicates: 1,566 (see duplicate_of)
Errors: 38 (see error)
Output: users.cleaned.csv
users.cleaned.csv keeps your columns and adds email_normalized, disposable, deliverable, relay, provider, duplicate_of and a few trust signals. duplicate_of points at the row of the first account with the same inbox. If it stops halfway (quota, Ctrl+C), run it again: finished rows are skipped. Details are in Clean an existing user list.
Step 3: act on each group, carefully
The check tells you what an address is. What you do about it is a product decision, and the safe default is gentle first.
Undeliverable addresses. These people can't receive your password resets either. Stop sending them email today (that alone protects your sender reputation), and ask them to fix their address the next time they log in. Deactivate only accounts that never log in.
Disposable addresses. Don't delete paying customers. For free accounts: remove the free credits or trial time, and ask the user to switch to a permanent address to keep using the free tier. Many abandoned this address the day they signed up anyway.
Duplicates. Review before merging. A group with the same email_normalized is the same inbox, but think about what each account holds:
- several free accounts, no payments: keep the oldest, close or merge the rest, and take back the duplicated free credits;
- one paying account plus free aliases: keep the paying one;
- several paying accounts: leave them alone and maybe ask why. Some agencies run one account per client on purpose.
Privacy relays. No action unless the account holds perks you'd normally require a second signal for.
Never send a mass "we detected abuse" email. Honest users with odd addresses exist, and an accusation costs you more than the free tier did.
Step 4: keep it clean
Import email_normalized into a unique column, so old aliases can't come back as new signups:
ALTER TABLE users ADD COLUMN email_canonical text;
-- load the cleaned CSV into a temp table, then:
UPDATE users u SET email_canonical = c.email_normalized FROM cleaned c WHERE c.id = u.id;
-- resolve the duplicate groups first, then:
CREATE UNIQUE INDEX users_email_canonical_key ON users (email_canonical);
From then on, check every new signup with the same rules (how to block disposable emails at signup) and store the canonical email it returns. The list stays clean without another cleanup.
How often to re-run it
Once is usually enough if new signups are checked from now on. Run it again only if you imported users from elsewhere, merged with another product, or added the signup check late after a growth spike.
Summary
- Old accounts need a one-time cleanup: the signup check only protects new ones.
- Look for disposable, undeliverable, duplicate and relay addresses, and treat each differently.
- Be gentle: stop emailing dead addresses, take back free perks, review duplicates before merging.
- Store the canonical email in a unique column so the cleanup sticks.
Bulk checks are part of Premium. For lists over 100,000 users, ask about Enterprise bulk credits that never expire.