Migrating from Mailchimp
Connect Mailchimp, read a dry run of exactly what would come across, then commit it. Nothing is written to your contacts until you commit. Mailchimp data streams through our importer in memory and goes straight into your tables; the Mailchimp credential is kept in your account’s SSM Parameter Store during the import and deleted when you commit.
Mailchimp is a trademark of its owner, and we aren’t affiliated with it.
Start the import
Section titled “Start the import”Create a Mailchimp API key under Profile → Extras → API keys (Mailchimp’s guide: About API keys). Then start the import. Leave out audienceIds to import every audience.
curl -X POST "$BASE/imports/mailchimp" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"method":"apiKey","apiKey":"<mailchimp-key>","audienceIds":["a1b2c3d4e5"],"filters":{"engagedWithinDays":365,"minRating":2},"importTemplates":true}'A key Mailchimp rejects gives 422 mailchimp_auth_failed. To connect with OAuth instead, send {"method":"oauth"} and open the authorizeUrl in the response. Mailchimp sends you back to our one redirect URL, https://api.rhinorelay.com/oauth/mailchimp/callback, which hands you straight on to your own Region. There we store the token in your account, and the import starts.
The link works for an hour. Until the import connects, reading it (GET /imports/{id}) with a key that can start imports (contacts:write or admin) returns a fresh authorizeUrl, so you can finish later; a read-only key gets none. An import nobody connects within a day fails, and its links stop working: start a new one.
Read the dry run
Section titled “Read the dry run”The import reads your members and their activity in your last 12 campaigns. Poll it until state is dry_run_ready. We also publish an import.dry_run_ready event to your EventBridge bus at that point.
If Mailchimp refuses the credential or a request (an audience ID that doesn’t exist, for example), the import’s state becomes failed, and its failureReason says why: mailchimp_refused for the credential, mailchimp_not_found for something that doesn’t exist, mailchimp_rejected for any other request Mailchimp turns down. An import nobody connected within a day has mailchimp_not_connected, and the API reference lists the rest. Start a new one. Rate limits, timeouts and Mailchimp outages are retried, carrying on where the import left off.
curl "$BASE/imports/<im_...>" -H "Authorization: Bearer $KEY"The summary tells you:
| Field | Means |
|---|---|
read |
Members read from Mailchimp |
toImport |
Contacts that will be written |
suppressed |
Of those, how many come across unsubscribed or cleaned, so they stay suppressed |
removedByCleaning |
Subscribed members the filters leave out |
skipped |
Members not imported, by reason: pending, transactional, invalid_email, unsendable_address (non-ASCII characters before the @, which Amazon SES can’t send to) and erased |
errors |
Problems, without addresses |
Commit it
Section titled “Commit it”If the numbers look right, commit. You can change the filters here, without reading Mailchimp again:
curl -X POST "$BASE/imports/<im_...>/commit" -H "Authorization: Bearer $KEY" -H 'Idempotency-Key: mailchimp-commit-1' \ -H 'Content-Type: application/json' -d '{"filters":{"engagedWithinDays":365,"minRating":2}}'
curl "$BASE/imports/<im_...>" -H "Authorization: Bearer $KEY"Check it worked: the import’s state is completed, and GET $BASE/contacts lists the imported contacts. Then revoke the API key in Mailchimp (or our app’s access, with OAuth): we’ve already deleted our copy.
What comes across
Section titled “What comes across”| Mailchimp status | Imported as | Why |
|---|---|---|
subscribed |
subscribed |
Opted in |
unsubscribed |
unsubscribed |
Must stay suppressed |
cleaned |
cleaned |
Hard-bounced |
archived |
unsubscribed, with the reason mailchimp_archived |
A safe default. You can resubscribe them one by one with new consent. |
pending |
Not imported | Never confirmed |
transactional |
Not imported | Never opted into marketing |
- Merge fields become custom fields:
FNAMEbecomesfields.FNAME. - Tags come across as tags, and interests as tags named
interest:<category>:<name>. - Opt-in data (
timestamp_opt,ip_opt,timestamp_signup,ip_signup,sourceand marketing permissions) becomes the consent record, with methodimported. A member with neithertimestamp_optnortimestamp_signupgets itslast_changedtime asconsent.at. - Engagement: the member rating sets the starting score, and the last open and click come from your recent campaigns.
- The same address in several audiences comes across once: the most restrictive status wins, tags are combined, and the earliest consent is kept.
- Erased addresses are never imported again.
- Templates: with
importTemplates, the HTML of your last 10 campaigns becomes templates, with*|FNAME|*merge tags rewritten as Liquid. Each is flagged for review: check the unsubscribe link and footer before you use it.
Cleaning filters
Section titled “Cleaning filters”Filters apply only to subscribed members. Suppressed members always come across, so they stay suppressed.
| Filter | Default | Keeps subscribed members who |
|---|---|---|
engagedWithinDays |
365 | opened, clicked or opted in within this many days |
minRating |
2 | have a Mailchimp member rating of at least this (1 to 5) |
includeUnengaged |
false |
set to true to keep everyone, whatever their engagement |
Members the filters leave out aren’t imported at all. Sending to people who haven’t engaged in a year is the fastest way to bounces and complaints.
Cancel an import
Section titled “Cancel an import”An import that hasn’t been committed can be cancelled. Its credential and any staging files are deleted.
curl -X POST "$BASE/imports/mailchimp" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"method":"apiKey","apiKey":"<mailchimp-key>"}'
curl -X DELETE "$BASE/imports/<im_...>" -H "Authorization: Bearer $KEY"Check it worked: the delete returns 204, and the import’s state is cancelled. A committed import gives 409 already_committed.
For a CSV export from Mailchimp or anywhere else, see CSV import.