How to clean a Mailchimp audience with email verification
· 4 min read · Tutorials
To clean a Mailchimp audience, export the subscribed contacts as CSV, run the addresses through an email verifier, re-import the bad ones with "update existing contacts" checked and a tag such as verify-invalid, then turn that tag into a segment and archive it. Mailchimp's own tools handle the export, the tagging and the archive. The verifier only has to tell you which addresses are invalid, which are risky, and which it couldn't decide.
Step 1: export the audience
Mailchimp's help article Export contacts gives the path: Audience, pick the audience, Export audience, then Export CSV. Only Owners and Admins can export. You get an email when the file is ready.
The export can arrive as a ZIP with one CSV per contact type: subscribed, unsubscribed, non-subscribed and cleaned. You only need the subscribed file. Cleaned contacts are addresses Mailchimp already stopped sending to after bounces, and unsubscribed contacts won't get mail anyway.
If you only want to check one segment (say, contacts imported from an old event), export that segment instead from the Segments drop-down.
Step 2: verify the addresses
You have two options with pingutil, depending on size and whether you want to write code.
Option A: dashboard CSV upload
Sign in, upload the CSV, pick the email column, and download the file with result columns appended. Each upload takes up to 50 rows, so this suits small audiences or a test sample.
Option B: the batch API
For a few hundred addresses, a short script is faster. POST /v1/verify/batch takes up to 50 addresses per request and returns results in the same order:
# pip install requests
import csv, os, sys, time, requests
KEY = os.environ["PINGUTIL_KEY"]
src, dst = sys.argv[1], sys.argv[2] # subscribed.csv, results.csv
with open(src, newline="", encoding="utf-8") as f:
emails = [row["Email Address"].strip() for row in csv.DictReader(f)]
with open(dst, "w", newline="", encoding="utf-8") as out:
w = csv.writer(out)
w.writerow(["Email Address", "status", "reason", "score", "did_you_mean"])
for i in range(0, len(emails), 50):
chunk = emails[i:i + 50]
r = requests.post(
"https://pingutil.com/v1/verify/batch",
headers={"Authorization": f"Bearer {KEY}"},
json={"emails": chunk},
timeout=120,
)
if r.status_code == 429:
print("Rate or daily limit reached:", r.json())
break
r.raise_for_status()
for res in r.json()["results"]:
w.writerow([res["input"], res["status"], res["reason"], res["score"], res["did_you_mean"] or ""])
time.sleep(1)
Run it with python verify.py subscribed.csv results.csv. It stops cleanly on a 429 so you can resume from where it got to.
Be realistic about volume. pingutil verifies up to 300 addresses a day per account, API and CSV uploads combined. That's fine for a small audience, a new-subscriber segment, or a sample to measure how dirty a list is. For a one-off clean of tens of thousands of contacts in a day, a bulk-focused service will be quicker; our verifier comparisons are honest about that.
Step 3: decide what each result means
Sort results.csv into three buckets:
| Result | What to do in Mailchimp |
|---|---|
invalid (mailbox_not_found, no_mx, null_mx, disposable_domain, mailbox_disabled, invalid_syntax) |
Archive |
risky / accept_all |
Keep. Catch-all domains are common in B2B. See catch-all email domains. |
risky / role_based |
Keep if they opted in; review if they came from an import |
unknown |
Keep for now and re-check in a day or two |
valid |
Keep |
Don't archive unknown addresses. That status means the mail server didn't give a clear answer (greylisting, a temporary block, a timeout), not that the mailbox is gone. The full list is in the verification status reference.
Step 4: tag the bad addresses in Mailchimp
Make a CSV with just an Email Address column holding the addresses you want to archive. Then follow Mailchimp's import contacts flow:
- Audience, Add contacts, Import contacts, Upload a file.
- Check the option to update any existing contacts.
- At the tag step, create a tag such as
verify-invalid-2026-10. - Finish the import.
Two cautions from Mailchimp's docs. The update replaces field data with what's in your file, so blank columns erase existing values: keep the file to the email column only. And an import doesn't change the marketing status of contacts already in your account, so you can't use it to unsubscribe existing contacts. That's why the next step is archive.
Step 5: archive the tagged contacts
Open the Tags drop-down, choose your tag, and Save as segment. Then, per Mailchimp's archive contacts article, select all contacts in the segment, open Actions, choose Archive, and confirm. Archived contacts don't count toward your billing tier and can be restored if you made a mistake.
Archiving is per audience. If the same address lives in two audiences, repeat the steps in each.
Keep it clean afterwards
- Verify new contacts as they arrive rather than cleaning in bulk once a year. A form webhook or a Zapier step works.
- Re-verify segments you haven't mailed in months before you send to them. Our newsletter list cleaning post covers the routine.
- Try a few addresses in the email verification demo first if you want to see the statuses before you commit.
Questions people ask
- Should I archive or delete invalid contacts in Mailchimp?
- Archive, unless you have a legal reason to delete. Mailchimp says archived contacts don't count toward your billing tier and can be restored later, while deletion is permanent. For GDPR erasure requests Mailchimp says archiving is not a substitute for deletion.
- Does Mailchimp remove bounced addresses on its own?
- Yes, after the fact. Mailchimp's help center says it cleans bounced addresses from your audience and stops sending to them. Verification is about catching dead addresses before the bounce, which matters for old or imported segments.