Bulk Email Sending Made Easy and *free* with AWS SES

Ram Potham
4 min readDec 26, 2022

--

Are you tired of manually sending emails one by one? Do you have a large list of recipients that you need to send emails to all at once? AWS SES (Amazon Web Services Simple Email Service) can help!

AWS SES is a cloud-based email service that enables you to send emails at scale. Whether you are a small business owner, a marketer, or a developer, AWS SES can help you easily and efficiently send bulk emails to your customers, clients, or users. It is free for the first 62,000 emails per month and costs $0.10 for ever 1,000 emails past that.

Here’s how to get started with AWS SES:

  1. Sign up for an AWS account: If you don’t already have an AWS account, you will need to create one. This is free and only requires a few minutes to set up.
  2. Verify your email address: Before you can start sending emails, you need to verify your email address. This helps ensure that only authorized users can send emails using your account.
  3. Set up your sending domains: If you want to send emails using your own domain name, you will need to set up your sending domains in AWS SES. This involves adding a few DNS records to your domain’s DNS configuration.
  4. Create a sending identity: A sending identity is the “from” address that your emails will be sent from. You can create a sending identity for each email address or domain that you want to send emails from.
  5. Create a contact list: Once you have set up your sending identity, you can create a contact list in AWS SES. To do this, go to the AWS SES console and select “Contacts” from the left-hand menu. From there, you can create a new contact list and add the email addresses of the people you want to include on the list.
  6. Add subscribers to your list: To add subscribers to your contact list, you can either import a list of email addresses from a file or manually add them one by one. You can also use the AWS SES API to programmatically add subscribers to your list, which we will set up.

However, SES is simply an email sending provider, so to actually send the emails you need compute — Lambda or EC2. We will be using Lambda since it is cheaper on a small scale and it’s serverless. The first 1 million requests per month are free

Whole Architecture Diagram

To bulk send emails, first we need to create an SQS queue to store emails so that a lambda sends the emails as they are popped of the queue. To do so:

  1. Navigate to the SQS dashboard.
  2. From the SQS dashboard, click the “Create a queue” button to start the process of creating a new queue.
  3. Name your queue, I named mine “OutgoingEmails”
  4. From the “Queue Type” dropdown menu, select the “Standard” queue type. This type of queue is suitable for most use cases and allows you to send, receive, and delete messages.
  5. You can configure your queue with various settings, such as the message retention period, the maximum message size, and the delivery delay. You can leave these settings at their default values or adjust them to fit your needs.
  6. Once you have configured your queue, click the “Create queue” button to create it. Your new queue will be ready to use and you can start adding messages to it.

Then, here is a lambda that inserts all the emails from your contact list into the SQS queue.

import json
import boto3
sqs = boto3.resource('sqs',region_name="us-east-1")
ses = boto3.client('sesv2',region_name="us-east-1")

def lambda_handler(event, context):
contact_list = ses.list_contacts(
ContactListName = "ContactList"
)
split_arr = [v["EmailAddress"] for v in contact_list["Contacts"]]
queue = sqs.get_queue_by_name(QueueName='Email Queue')
for arr in split_arr:
response = queue.send_messages(Entries=[{
'Id': str(i),
'MessageBody': e,
} for i,e in enumerate(arr)])

return {
"message": "success"
}

Make sure the lambda has IAM access to the queue.

Then, set up another lambda with the SQS queue as a trigger so that it’s triggered for every email:

import json
import boto3
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

AWS_REGION = "us-east-1"
client = boto3.client('sesv2',region_name=AWS_REGION)

def lambda_handler(event, context):
print(event)
emailData = event['Records'][0]["body"]
print(event['Records'][0])
SENDER = "Sender Name <Sender Email>"
RECIPIENT = emailData
DESTINATION = {
'ToAddresses': [
RECIPIENT
]
}
lambda_client = boto3.client('lambda')
lambda_payload = {"update":"false"}
BODY_TEXT = "Email Text if HTML is not allowed"
BODY_HTML = "<p>Email HTML</p>"
CHARSET = "utf-8"
SUBJECT = "My Subject"
msg = MIMEMultipart('mixed')
msg['Subject'] = SUBJECT
msg['From'] = SENDER
msg['To'] = RECIPIENT
msg['Reply-To'] = "reply-to email"
msg_body = MIMEMultipart('alternative')
textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET)
htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET)
msg_body.attach(textpart)
msg_body.attach(htmlpart)
msg.attach(msg_body)

response = client.send_email(
FromEmailAddress=SENDER,
Content={
"Raw": {
"Data": msg.as_string()
}
},
ListManagementOptions={
'ContactListName': 'ContactList',
}
)

return {
'statusCode': 200,
'body': json.dumps(response)
}

Again, make sure the lambda has IAM access to SES

Now, whenever you run the first lambda with emails in your contact list, your desired emails will be sent to all of them, basically for free!

Here is an implementation I’ve created. Also, this article is part of a 4 part series. The next article is Adding Subscribe and Unsubscribe.

--

--

Ram Potham

Founder. AWS certified expert. Web3 enthusiast. Yoga Teacher. Artificial Intelligence student at Carnegie Mellon University.