You have a campaign scheduled for Thursday and a list nobody has touched in a year. Some of it came from a webinar export, some from the CRM, and a few thousand rows from a trade show spreadsheet. Sending to it as is risks a burst of hard bounces, a spike in spam complaints, and a sender reputation that takes weeks to recover.
Cleaning that list is not one action. It is a short sequence: get every row in one place, remove duplicates, fix what you can prove, verify, then apply a decision you made in advance to every result. After that come two steps verification cannot do for you: removing people who stopped reading, and making sure nobody who opted out sneaks back in.
This guide walks through the sequence in the order we would run it, with a worked example on a 48,200-row list.
Step 1: Export everything and dedupe it properly
Start by exporting from every system that will send to this audience: the ESP, the CRM, any spreadsheet that feeds them. Export the suppression data too (unsubscribes, spam complaints, past hard bounces). You need it in step 6, and it is far easier to grab now than to rebuild later.
Then deduplicate. An exact-match dedupe misses most duplicates, because the same person shows up in slightly different forms.
Case. Jane.Doe@Acme.com and jane.doe@acme.com are the same mailbox at almost every provider. Strictly, RFC 5321 says the local part (before the @) may be case sensitive, while the domain never is. It also says exploiting that is discouraged, and in practice we have not seen a mainstream provider do it. Lowercase both parts and trim whitespace before you compare.
Plus addresses. jane+webinar@gmail.com and jane@gmail.com reach the same inbox. That is plus addressing. Do not strip the tag automatically: the person chose that address, may filter on it, and your consent record is attached to the tagged form. If both forms are on the list, keep the row that holds the subscription record and drop the other. RocketVerifier flags tagged addresses as deliverable/ alias so you can find these pairs after verification too.
Gmail dots. Google states that dots in a Gmail address are ignored, so j.a.n.e@gmail.com and jane@gmail.com are one inbox (Google). Only collapse dots for gmail.com and googlemail.com. The same Google page says dots do change the address on work and school accounts, so on a company domain jane.doe@ and janedoe@ can be two different people.
Step 2: Fix the typos you can prove
Some addresses are broken in an obvious way: gmial.com, hotmial.com, a company domain ending in .con. Verification will catch these anyway (as risky/ possible_typo , with a suggested fix in did_you_mean), but deciding your policy now saves a second pass.
The tempting move is to correct every one. Be careful. maria.okonkwo-reyes@gmial.com almost certainly belongs to the Maria at gmail.com. john@gmial.com does not: the corrected john@gmail.com is a stranger who never subscribed.
Our rule: fix a typo only when you have a second signal that the corrected address is the same person. A distinctive local part counts. So does a first and last name in the CRM that matches the local part. Everything else goes to suppression with the typo noted, and if the contact matters, someone asks them for the right address through another channel.
Step 3: Verify the list, and preview it first
Upload the deduplicated file to bulk email verification. Jobs take up to 100,000 addresses in CSV, TSV, TXT or XLSX.
Before anything is charged, RocketVerifier runs a free list preview: the free checks (syntax, domain, disposable) run on every row, and a small random sample gets a full SMTP probe. You see an estimated split of results before you commit credits, which is the moment to stop if the file turns out to be the wrong export.
When you run the job, each address gets a status and a sub_status. Verification uses the same SMTP conversation a mail server would, stopping before any message is sent; how email verification works covers what each check asks. Billing is 1 credit per address, and unknown results are never charged.
Step 4: Decide what to do with each verification result
This is the part that decides whether the clean was worth it. Settle the mapping before you see the numbers, so a disappointing result does not tempt you into sending to addresses you would normally hold back.
There are four actions:
- Keep: goes into the first wave.
- Suppress: never mailed. Stored with the reason.
- Segment: mailed later, in a smaller wave, after the first one comes back clean.
- Re-check: verified again in a day or two, then routed by the new result.
| Result | Action | Why |
|---|---|---|
| deliverable/ none | Keep | The server explicitly accepted this mailbox. |
| deliverable/ alias | Keep, after the plus-address check | Real mailbox. If the untagged form is also listed, keep only the row with the consent record. |
| undeliverable/ mailbox_not_found | Suppress | The server said no such user. This is a guaranteed hard bounce. |
| undeliverable/ account_disabled | Suppress | The mailbox exists but is switched off, usually a person who left. |
| undeliverable/ does_not_accept_mail | Suppress | The domain has a null MX or no mail route at all. |
| undeliverable/ invalid_domain | Suppress | The domain does not exist. Check step 2 first in case it is a provable typo. |
| undeliverable/ invalid_syntax | Suppress | Not a valid address. Often a form field filled with something else. |
| undeliverable/ disposable | Suppress | A throwaway inbox. It works today and will be gone soon. |
| risky/ do_not_mail | Suppress, permanently | On your suppression list or a known complainer. See step 6. |
| risky/ possible_trap | Suppress | Matches a known trap pattern, such as a typo-trap domain or a spamtrap@ local part. |
| risky/ toxic | Suppress | Placeholder, abusive or keyboard-mash address. Nobody real signed up with it. |
| risky/ possible_typo | Fix and re-verify, or suppress | Apply did_you_mean only under the rule in step 2. |
| risky/ catch_all | Act on the verdict | send_safe with your deliverables in wave 1, send_with_caution in wave 3, avoid suppressed. See below. |
| risky/ role_based | Segment | Real, but shared. Wave 2 if they opted in, otherwise suppress. See below. |
| risky/ mailbox_full | Re-check in two weeks | Full inboxes are often abandoned ones. Still full on the second check means suppress. |
| unknown/ greylisted | Re-check | Already retried up to three times about ten minutes apart. Try again tomorrow. |
| unknown/ blocked | Re-check | The server refused the check itself, which says nothing about the mailbox. |
| unknown/ dns_error | Re-check | The DNS lookup failed. Usually temporary. |
| unknown/ smtp_timeout | Re-check | The server did not answer in time. |
| unknown/ no_connect | Re-check | No connection to the mail server. |
| unknown/ smtp_unavailable | Re-check | The server was not taking SMTP sessions at that moment. |
Catch-all addresses
A catch-all domain accepts mail for any address, so its 250 OK proves little. RocketVerifier returns these as risky / catch_all with a catch_all_verdict, which comes from a 0 to 100 score: send_safe (65 and up), send_with_caution (35 to 64) and avoid (under 35). If you use an older API version, catch-alls arrive as unknown instead; the dashboard and the current API show the verdict.
One override we apply: your own engagement beats our score. A catch-all address that clicked something in the last 90 days goes in wave 1, whatever its verdict, because a click is better evidence of a real reader than anything a verifier can see from outside. The full reasoning is in catch-all emails: should you send to them?.
Role accounts
Addresses like info@, sales@ or billing@ reach a shared inbox. Several people may read it, any of them can press “report spam”, and none of them may remember the signup. That is a judgment about risk, not a rule: a B2B customer who registered purchasing@ for invoices wants that mail. Keep role accounts that opted in through a form and mail them in wave 2. Suppress role accounts that arrived through a scrape, a list swap or a contact-us form. More detail in what is a role-based email address.
Unknowns
Unknown means the verifier asked and got no trustworthy answer. Most unknowns are real addresses on cautious servers. Re-run them after a day or two; because unknowns are not charged, a re-check only costs credits for the addresses that now get a real answer. Whatever is still unknown after the second pass goes into the last, smallest wave, where real sending gives the answer.
A worked example: 48,200 rows to a send plan
Here is the whole process on one list. The numbers are illustrative, not from a customer, but each stage adds up to the one before it, so you can follow every address.
Illustrative numbers
- 1
Raw export
Every row from the ESP and CRM exports, merged.
rows 48,200 - 2
Dedupe and tidy -1,990 rows
Lowercase, trim, drop blank rows and merge case-variant duplicates.
rows 46,210 - 3
Verify
Upload the 46,210 unique addresses. Status split below.
split 46,210- deliverable 37,400
- risky 5,060
- undeliverable 2,690
- unknown 1,060
- 4
Re-check unknowns
Two days later, the 1,060 unknowns run again. 700 resolve.
split 46,210- deliverable 37,960
- risky 5,060
- undeliverable 2,830
- still unknown 360
- 5
Route
Status, sub_status and engagement decide where each address goes.
split 46,210- Wave 1: deliverable and send_safe, engaged 34,480
- Wave 2: opted-in role accounts 1,310
- Wave 3: send_with_caution, still unknown 1,450
- Re-engagement only: inactive 12+ months 4,900
- Hold: mailbox_full, re-check later 180
- Suppress 3,890
34,480
ready for the first send (71.5% of raw rows)
37,240
mailed across waves 1 to 3, if each wave stays clean
45,850
credits used: 45,150 first pass plus 700 resolved re-checks
A few things the example shows:
- Dedupe did real work. 1,870 rows were exact repeats or case variants, the overlap you get when several tools each export the same contacts with their own capitalisation.
- The 5,060 risky addresses were not one decision. They split into 1,420
send_safecatch-alls, 1,090send_with_caution, 640avoid, 1,310 role accounts, 180 full mailboxes and 420 suppress-on-sight results (possible_typo,possible_trap,toxic,do_not_mail). - Re-checking unknowns paid off. 700 of 1,060 resolved two days later: 560 deliverable, 140 undeliverable.
- The biggest single cut came after verification. 4,900 deliverable addresses had not opened or clicked in over a year. Verification could not see that. Step 5 explains why it matters.
On billing: the first pass used 45,150 credits (46,210 addresses minus 1,060 unknowns), and the re-check used 700 more, one per resolved address. A 50,000-credit pay-as-you-go pack ($169) covers it.
See the estimate before you pay
Upload your list and the free preview shows the expected result split first. Unknowns and duplicate addresses are never charged.
Step 5: Prune by engagement, because verification cannot
A mailbox that accepts mail is not a mailbox that anyone reads. People change jobs and leave an inbox running, or keep a personal address only for receipts. These addresses verify as deliverable for years, and mailbox providers notice when you keep mailing people who never open anything. Google’s sender guidelines tell senders to consider unsubscribing recipients who don’t open or read their messages, and Yahoo recommends periodically reconfirming inactive subscribers. This gradual loss of value is list decay, and the list decay calculator estimates how much of your list it has touched.
Neither provider publishes a number of days, so any threshold is a judgment. Ours, for a list mailed at least monthly:
- No opens or clicks in 6 months: move to a re-engagement segment. Send two or three messages that ask plainly whether they want to stay, with a one-click way to say yes.
- No response to re-engagement, or no engagement in 12 months: suppress. Not delete, suppress, so the address cannot return through an import.
- Anyone who clicks at any point: back to the main list.
Shorten these windows if you send several times a week, and lengthen them for a list mailed quarterly. Measure them against the contact’s join date, so a subscriber from last month is not judged on six months of silence.
Step 6: Keep the suppression list intact
The worst outcome of a list clean is mailing someone who unsubscribed or reported you. That happens through merges. A CRM that never received the unsubscribe flag, a spreadsheet exported before the opt-out, an old event list: any of them can bring an opted-out address back as a fresh contact.
Three habits prevent it:
- Keep one master suppression list across every tool that sends to this audience: unsubscribes, spam complaints, hard bounces and the suppressions from this clean.
- Apply it after every import or merge, not only before big sends. If you upload it to RocketVerifier, those addresses come back as risky/ do_not_mail and fall out of the plan automatically.
- Never remove anyone from it to grow the list. An unsubscribe is permanent until the person subscribes again themselves.
Opt-outs also have a deadline. Yahoo requires bulk senders to honor unsubscribes within 2 days, and Google recommends processing them within 48 hours (Google FAQ).
Step 7: Send to the cleaned list in waves
A cleaned list that has not been mailed in months is still unproven. Send the most certain segment first, and let its results decide whether the rest goes out.
- Wave 1: deliverable and
send_safecatch-alls, engaged. If this is much larger than your usual send volume, split it over a few days. - Wave 2: opted-in role accounts, a day or two after wave 1, once its numbers are clean.
- Wave 3:
send_with_cautioncatch-alls and the addresses still unknown after the re-check. Keep it small. - Re-engagement segment: its own campaign, on its own schedule.
Between waves, watch two numbers.
Spam complaint rate. Google asks senders to keep the user-reported spam rate in Postmaster Tools below 0.1% and never to reach 0.3%. The rate is calculated daily, and bulk senders above 0.3% become ineligible for Google’s mitigation support until the rate stays below 0.3% for 7 consecutive days (Google). Yahoo’s ceiling is also 0.3%. Treat 0.1% as the line where you stop and look, not 0.3%. The Gmail, Yahoo and Microsoft sender requirements guide covers the rest of the rules.
Bounces and deferrals. Google’s guidance is to reduce sending volume when messages start bouncing or being deferred, until the error rate drops. Our own working rule is to pause the next wave if hard bounces in the current one pass 2%, and to find out which source the bouncing addresses came from before continuing. The safe bounce rate page covers the benchmarks.
Pre-send checklist
- Every source exported, including unsubscribes, complaints and past hard bounces
- Addresses lowercased and trimmed, duplicates merged
- Plus-address and Gmail dot variants resolved to the row with the consent record
- Typos fixed only where a second signal confirms the person
- Free list preview checked before running the full job
- Every status and sub_status routed: keep, suppress, segment or re-check
- Unknowns and full mailboxes scheduled for a re-check
- Contacts with no engagement in 6 to 12 months moved to re-engagement or suppressed
- Master suppression list applied after the final merge
- Waves scheduled, with Postmaster Tools and bounce reports checked between them
How often to repeat this
The first clean of a neglected list is the expensive one. After that, the job is maintenance: verify new contacts as they arrive, re-verify the list on a schedule matched to how fast it decays, and run the engagement sunset continuously instead of once a year. How often should you verify your email list helps you pick the schedule.