DEV Community

Said Olano
Said Olano

Posted on

AWS SES: Email Service Integration (2026-08-24 18:45)

AWS SES: Email Service Integration

Amazon Simple Email Service (SES) is a scalable, cost-effective email platform that lets you send transactional emails, marketing messages, and notifications. This guide walks through integrating SES into your applications with practical examples.

Why Choose Amazon SES?

  • Cost-effective: Pay only for what you send ($0.10 per 1,000 emails at time of writing).
  • Scalable: Handles anything from a handful to millions of emails.
  • Deliverability: Built-in reputation management and dedicated IP options.
  • Integration: Works seamlessly with other AWS services (SNS, Lambda, S3).

Prerequisites

Before you begin, ensure you have:

  1. An AWS account with appropriate IAM permissions.
  2. A verified domain or email address.
  3. AWS SDK installed for your language of choice.

Step 1: Verify Your Identity

SES requires you to verify ownership of the domain or email address you send from.

# Verify a single email address
aws ses verify-email-identity --email-address sender@example.com

# Verify an entire domain
aws sesv2 create-email-identity --email-identity example.com
Enter fullscreen mode Exit fullscreen mode

For domain verification, add the returned DKIM CNAME records to your DNS provider. This improves deliverability and prevents spoofing.

Step 2: Move Out of the Sandbox

New accounts start in the SES sandbox, which restricts you to verified recipients and low sending limits. To send to arbitrary addresses, request production access via the AWS Console under SES > Account dashboard > Request production access.

Step 3: Sending Email with the SDK

Here's a Python example using boto3:

import boto3
from botocore.exceptions import ClientError

client = boto3.client("ses", region_name="us-east-1")

def send_email(sender, recipient, subject, body_html):
    try:
        response = client.send_email(
            Source=sender,
            Destination={"ToAddresses": [recipient]},
            Message={
                "Subject": {"Data": subject, "Charset": "UTF-8"},
                "Body": {
                    "Html": {"Data": body_html, "Charset": "UTF-8"}
                },
            },
        )
        print(f"Email sent! Message ID: {response['MessageId']}")
    except ClientError as e:
        print(f"Error: {e.response['Error']['Message']}")

send_email(
    sender="sender@example.com",
    recipient="recipient@example.com",
    subject="Welcome!",
    body_html="<h1>Hello</h1><p>Thanks for signing up.</p>",
)
Enter fullscreen mode Exit fullscreen mode

Step 4: Using Configuration Sets

Configuration sets let you track email events (bounces, complaints, deliveries) and publish them to SNS, CloudWatch, or Kinesis Firehose.

aws ses create-configuration-set \
  --configuration-set Name=my-config-set

aws ses create-configuration-set-event-destination \
  --configuration-set-name my-config-set \
  --event-destination '{
    "Name": "sns-destination",
    "Enabled": true,
    "MatchingEventTypes": ["bounce", "complaint"],
    "SNSDestination": {"TopicARN": "arn:aws:sns:us-east-1:123456789012:ses-events"}
  }'
Enter fullscreen mode Exit fullscreen mode

Reference the configuration set when sending:

response = client.send_email(
    Source=sender,
    Destination={"ToAddresses": [recipient]},
    Message={...},
    ConfigurationSetName="my-config-set",
)
Enter fullscreen mode Exit fullscreen mode

Step 5: Handling Bounces and Complaints

Managing bounces and complaints is critical to maintaining a healthy sender reputation. AWS may pause your account if these rates exceed thresholds (5% bounce, 0.1% complaint).

A typical pattern:

  1. Configure SES to publish bounce/complaint notifications to an SNS topic.
  2. Subscribe a Lambda function to the topic.
  3. In the Lambda, remove problematic addresses from your mailing list.
import json

def lambda_handler(event, context):
    for record in event["Records"]:
        message = json.loads(record["Sns"]["Message"])
        notification_type = message["notificationType"]

        if notification_type == "Bounce":
            for recipient in message["bounce"]["bouncedRecipients"]:
                suppress_address(recipient["emailAddress"])
        elif notification_type == "Complaint":
            for recipient in message["complaint"]["complainedRecipients"]:
                suppress_address(recipient["emailAddress"])

def suppress_address(email):
    # Add to your suppression list / database
    print(f"Suppressing {email}")
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Use DKIM and SPF: Authenticate your emails to avoid spam folders.
  • Set up a suppression list: Use SES account-level suppression to automatically skip known bad addresses.
  • Monitor your reputation: Watch the SES reputation dashboard and CloudWatch metrics.
  • Warm up dedicated IPs: Gradually increase volume to build a positive sending history.
  • Throttle intelligently: Respect your maximum send rate to avoid throttling errors.

Cost Considerations

Item Cost
Outbound emails $0.10 per 1,000
Attachments/data $0.12 per GB
Dedicated IP ~$24.95/month
Inbound emails $0.10 per 1,000

Always check the current pricing as rates may change.

Conclusion

Amazon SES provides a robust, affordable foundation for s

Top comments (0)