🚀 Executive Summary
TL;DR: Migrating from Mailchimp to AWS SES for cost-efficiency and deeper AWS integration is streamlined using Python. This guide details extracting subscriber data from Mailchimp via its API, storing it in CSV, and preparing for SES sending with boto3 for automated, high-volume email campaigns.
🎯 Key Takeaways
- Programmatically extract Mailchimp subscriber data using the Mailchimp API and Python’s
requestslibrary, handling pagination withoffsetandcountparameters for ‘subscribed’ members. - Store extracted subscriber information in a structured CSV file using Python’s
csv.DictWriterto ensure portability and easy integration with other applications. - Utilize
boto3for AWS SES to manage identity verification (email/domain) and send conceptual emails, understanding that SES requires verified senders and does not directly manage subscriber lists.
Moving from Mailchimp to AWS SES: Importing Subscribers via Python
As a Senior DevOps Engineer at TechResolve, I’ve seen many organizations grapple with the escalating costs of email marketing platforms like Mailchimp. While Mailchimp offers robust features, for high-volume sending or deeper integration with an existing AWS infrastructure, AWS Simple Email Service (SES) presents a compelling, cost-effective alternative. The challenge, however, lies in efficiently migrating your existing subscriber lists without tedious manual work or errors.
This tutorial will guide you, step-by-step, on how to programmatically extract your valuable subscriber data from Mailchimp and prepare it for seamless integration with AWS SES, all powered by Python. Our goal is to set up your subscriber base for a custom SES-powered email sending application, ensuring a smooth and automated transition.
Prerequisites
Ensure you have the following in place:
- Mailchimp Account: With an active audience and an API Key (Profile > Extras > API Keys) and the specific Audience ID.
-
AWS Account: With an IAM user having necessary SES permissions (e.g.,
ses:SendEmail,ses:VerifyEmailIdentity) and configured AWS CLI. - Python 3.x: Installed on your development machine.
-
Required Python Libraries: Install
requestsfor Mailchimp API andboto3for AWS SES.
pip install requests boto3
Step-by-Step Guide: Migrating Subscribers
Step 1: Extract Subscribers from Mailchimp
The first step involves programmatically fetching your subscriber data using the Mailchimp API and Python’s requests library. You will need your Mailchimp API key and the audience ID. The API key’s suffix (e.g., -us19) determines your data center, which is part of the API URL. Ensure your API key and audience ID are stored securely, ideally as environment variables.
import requests
import json
import os
# Configuration: Store securely, e.g., environment variables
MAILCHIMP_API_KEY = os.getenv("MAILCHIMP_API_KEY") # e.g., "YOUR_MAILCHIMP_API_KEY-us19"
MAILCHIMP_AUDIENCE_ID = os.getenv("MAILCHIMP_AUDIENCE_ID") # e.g., "YOUR_MAILCHIMP_AUDIENCE_ID"
# Extract data center from API key
if MAILCHIMP_API_KEY:
dc = MAILCHIMP_API_KEY.split('-')[-1]
MAILCHIMP_API_BASE_URL = f"https://{dc}.api.mailchimp.com/3.0/lists/{MAILCHIMP_AUDIENCE_ID}/members"
else:
raise ValueError("MAILCHIMP_API_KEY environment variable not set.")
def get_mailchimp_subscribers():
"""Fetches all subscribed members from a Mailchimp audience, handling pagination."""
subscribers = []
offset = 0
count = 500 # Max count per request
headers = {
"Content-Type": "application/json",
"Authorization": f"apikey {MAILCHIMP_API_KEY}"
}
print(f"Starting Mailchimp subscriber fetch for audience ID: {MAILCHIMP_AUDIENCE_ID}")
while True:
params = {"offset": offset, "count": count, "status": "subscribed"}
try:
response = requests.get(MAILCHIMP_API_BASE_URL, headers=headers, params=params, timeout=30)
response.raise_for_status() # Raise HTTPError for bad responses
data = response.json()
if not data.get("members"):
break # No more members
for member in data["members"]:
subscriber_info = {
"email_address": member.get("email_address"),
"status": member.get("status"),
"first_name": member.get("merge_fields", {}).get("FNAME"),
"last_name": member.get("merge_fields", {}).get("LNAME")
}
subscribers.append(subscriber_info)
if len(data["members"]) < count: # If fewer members than count, it's the last page
break
offset += count
except requests.exceptions.RequestException as e:
print(f"Error fetching Mailchimp subscribers: {e}")
break
except json.JSONDecodeError:
print(f"Error decoding JSON: {response.text}")
break
print(f"Successfully fetched {len(subscribers)} subscribed members.")
return subscribers
# To run this, you would typically call:
# all_subscribers = get_mailchimp_subscribers()
# if all_subscribers:
# # Proceed to save or process
# pass
Code Logic Explanation:
- The Mailchimp API URL is constructed using your audience ID and the data center from your API key.
- A loop fetches subscribers, handling pagination with
offsetandcountparameters. - Only
"subscribed"members are retrieved, extracting email, status, and merge fields. - Robust error handling is included for network and JSON issues.
Step 2: Process and Store Subscribers Locally
After extraction, storing your data in a clean, portable format like a CSV file is crucial. This step takes the list of subscriber dictionaries from Step 1 and writes them into a structured CSV file, easily usable by other applications or for import into databases.
import csv
import os
def save_subscribers_to_csv(subscribers_data, filename="mailchimp_subscribers.csv"):
"""Saves subscriber data to a CSV file."""
if not subscribers_data:
print("No subscriber data to save.")
return
headers = ["email_address", "status", "first_name", "last_name"]
try:
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=headers)
writer.writeheader()
writer.writerows(subscribers_data)
print(f"Successfully saved {len(subscribers_data)} subscribers to {filename}")
except IOError as e:
print(f"Error writing to CSV file {filename}: {e}")
# Example usage (assuming all_subscribers from Step 1):
# if 'all_subscribers' in locals() and all_subscribers: # Check if all_subscribers exists
# save_subscribers_to_csv(all_subscribers)
# else:
# print("No Mailchimp data found to save. Run Step 1 first.")
Code Logic Explanation:
- Uses Python’s
csv.DictWriterto map dictionary keys to CSV headers. - The
headerslist defines column order. - The script opens the file in write mode, ensuring correct newline handling and UTF-8 encoding.
- It writes the header row, then all subscriber data rows.
Step 3: Prepare for AWS SES Usage (Identity Verification & Conceptual Send)
AWS SES is a powerful email sending service, distinct from Mailchimp as it does not manage subscriber lists directly. Your application will provide the email addresses (e.g., from your generated CSV file) to SES for sending. A critical prerequisite for using SES is Identity Verification, where you prove ownership of your sending email addresses or domains.
Below, we demonstrate basic boto3 interactions for identity verification and a conceptual email send. For bulk sending, you would load your CSV file, iterate through recipients, and integrate with SES, carefully managing sending limits and bounce handling.
import boto3
import os
# AWS SES Configuration
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
ses_client = boto3.client("ses", region_name=AWS_REGION)
def verify_email_identity(email_address):
"""Initiates verification for an email identity in SES."""
try:
response = ses_client.verify_email_identity(EmailAddress=email_address)
print(f"Verification email sent to {email_address}. Check your inbox.")
return response
except Exception as e:
print(f"Error verifying email {email_address}: {e}")
return None
def list_verified_identities():
"""Lists all verified identities (email and domain) in your SES account."""
try:
email_response = ses_client.list_identities(IdentityType='EmailAddress')
domain_response = ses_client.list_identities(IdentityType='Domain')
print("Verified Email Identities:")
for identity in email_response.get("Identities", []):
print(f"- {identity}")
print("Verified Domain Identities:")
for identity in domain_response.get("Identities", []):
print(f"- {identity}")
return email_response, domain_response
except Exception as e:
print(f"Error listing identities: {e}")
return None, None
def send_conceptual_email(source_email, destination_email, subject, body_html):
"""Sends a test email using AWS SES."""
# Source email MUST be a verified identity in SES.
# For sandbox accounts, destination_email must also be verified.
try:
response = ses_client.send_email(
Source=source_email,
Destination={
'ToAddresses': [destination_email],
},
Message={
'Subject': {'Data': subject, 'Charset': 'UTF-8'},
'Body': {'Html': {'Data': body_html, 'Charset': 'UTF-8'}}
}
)
print(f"Conceptual email sent to {destination_email}. Message ID: {response['MessageId']}")
return response
except Exception as e:
print(f"Error sending conceptual email to {destination_email}: {e}")
return None
# --- To run this code ---
# 1. Set AWS_REGION environment variable.
# 2. Uncomment and call functions as needed:
# verify_email_identity("your_sending_email@example.com") # Verify your sender
# list_verified_identities()
# Conceptual loop for sending to imported subscribers:
# import csv
# with open('mailchimp_subscribers.csv', 'r', encoding='utf-8') as f:
# reader = csv.DictReader(f)
# for row in reader:
# sender_email = "your_verified_sender@yourdomain.com" # Must be verified!
# recipient_email = row['email_address']
# # send_conceptual_email(sender_email, recipient_email, "Hello from SES!", "<p>Welcome!</p>")
Code Logic Explanation:
- Initializes the
boto3SES client for your specified AWS region. -
verify_email_identityinitiates email address verification; SES sends a link. -
list_verified_identitiesshows your currently verified email addresses and domains. -
send_conceptual_emaildemonstrates sending a basic HTML email. TheSourceemail must be a verified SES identity. In a real application, you would iterate through your subscriber CSV, sending emails, while carefully managing SES limits and error handling.
Common Pitfalls
Migrating email services requires attention to detail. Here are a couple of common issues:
- Mailchimp API Rate Limits: Large subscriber lists can quickly hit Mailchimp’s API rate limits (e.g., 10 requests per 10 seconds). For millions of subscribers, introduce delays between API calls or process in smaller batches.
- AWS SES Sandbox and Sending Limits: New SES accounts start in a sandbox, limiting sends to verified recipients only and imposing low quotas (e.g., 200 emails/24 hours). To send to unverified recipients and increase quotas, you must request production access from AWS.
-
Authentication and Permissions: Ensure correct Mailchimp API keys (with read access) and AWS IAM user credentials (with appropriate SES permissions like
ses:SendEmail). Always use environment variables or a secrets manager for credentials.
Conclusion
Automating your Mailchimp to AWS SES subscriber migration with Python transforms a tedious chore into an efficient, repeatable process. This tutorial provided the essential scripts for data extraction, local storage, and conceptual integration with AWS SES.
Building upon this foundation, you can develop a comprehensive email sending application. Consider implementing dynamic templating, robust bounce and complaint handling via SNS, and advanced subscriber segmentation using AWS databases. Embrace the flexibility and cost-efficiency of AWS to fully control your email communications and enhance your DevOps strategy.
👉 Read the original article on TechResolve.blog
☕ Support my work
If this article helped you, you can buy me a coffee:

Top comments (0)