django-ses: a Django mail backend that talks to the SES API instead of SMTP
A Django email backend for Amazon's Simple Email Service
At a glance
- What is it?
- django-ses is a drop-in Django email backend that sends mail through Amazon Simple Email Service rather than an SMTP server, with SNS webhook events, automatic rate limiting and SESv2 support. It fits teams already on AWS that want deliverability data, and it is unnecessary for teams that can use the standard SMTP backend.
- Who is it for?
- Adopt django-ses if you run Django on AWS, need bounce and complaint visibility, or want to hand backends over to SES without touching application code. Do not adopt it for a single small app that already works with the default SMTP backend, and do not enable the SNS webhook without setting AWS_SNS_EVENT_TRUSTED_TOPIC_ARNS.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.
Star history
- django-ses/django-ses1,096 ★ · +4
| Repository | 2026-10-20 | 2026-11-06 |
|---|---|---|
| django-ses/django-ses | 1.1k | 1.1k |
4 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The backend swaps the SMTP hop for a direct SES API call
Django applications send mail through a pluggable backend, and the standard choice is an SMTP server. django-ses replaces that hop with a call to Amazon Simple Email Service. Nothing in your view code changes: once the backend is configured, django.core.mail.send_mail routes messages through SES, and the project describes itself as a drop-in replacement where your code should require no changes.
The audience is narrow and worth stating plainly. The README lists the cases that make SES attractive: you do not want to run mail servers, you are already deployed on EC2 where inbound traffic to SES is free, you need to send a high volume of email, you would rather not manage PTR records, reverse DNS or whitelist services, or you want DKIM signing through the SES Easy DKIM feature. The differentiator against plain SMTP is deliverability data, which is hard to obtain over the SMTP interface.
There is also an inbound side. django-ses can receive mail, which the project frames as an alternative to maintaining IMAP or POP servers when you want programmatic access to received mail or want to react to it immediately.
One settings block decides backend, region and credentials
Installation is a single pip line, and the backend is selected by a string in settings.py:
pip install django-sesEMAIL_BACKEND = 'django_ses.SESBackend'
AWS_ACCESS_KEY_ID = 'YOUR-ACCESS-KEY-ID'
AWS_SECRET_ACCESS_KEY = 'YOUR-SECRET-ACCESS-KEY'
AWS_SESSION_PROFILE = 'YOUR-PROFILE-NAME'
AWS_SES_REGION_NAME = 'us-west-2'
AWS_SES_REGION_ENDPOINT = 'email.us-west-2.amazonaws.com'Credentials are optional if you rely on AWS IAM Roles, and you can point the backend at a named CLI profile with AWS_SESSION_PROFILE. A separate key pair, AWS_SES_ACCESS_KEY_ID and AWS_SES_SECRET_ACCESS_KEY, exists for the case where sending mail should use different credentials from the ones used for S3 uploads.
Region handling has a trap. The region endpoint setting omits the scheme while the constructor argument requires it, and the project spells this out: the AWS_SES_REGION_ENDPOINT value does not include https:// whereas aws_region_endpoint passed through OPTIONS must. Mixing the two forms is an easy mistake to ship.
Django 6.1 introduced the MAILERS setting, which replaces EMAIL_BACKEND in Django 7.0. The same AWS settings are still read, so nothing else changes:
MAILERS = {
"default": {"BACKEND": "django_ses.SESBackend"},
}Per-mailer OPTIONS are passed to SESBackend verbatim:
MAILERS = {
"default": {
"BACKEND": "django_ses.SESBackend",
"OPTIONS": {"aws_region_name": "us-west-2"},
},
}Throttling is derived from the live SES rate limit
SES rejects mail once the account sending rate is exceeded. django-ses handles this by querying the API for the current limit and then sending no more than that number of messages in a two-second window. The window is half the reported limit, and the project describes it as a safety margin to stay clear of the ceiling.
The behaviour is controlled by a single setting:
AWS_SES_AUTO_THROTTLE = 0.5 # (default; safety factor applied to rate limit)Setting it to None turns automatic throttling off. Two practical notes follow from the design. The throttle depends on a live API query, so it introduces a per-window round trip that an application sending a handful of messages from a serverless function will pay for without much benefit. And because the limit is read from the API, the throughput you get follows your SES account quota rather than anything you configure, so a sandboxed account and a production account behave differently with identical code.
The project also supports the SESv2 client through USE_SES_V2, and a global multi-region endpoint for availability, which requires that flag:
USE_SES_V2 = True
AWS_SES_GLOBAL_ENDPOINT_ID = 'abcdef12.g3h'SNS webhook events need the events extra and a topic allowlist
Bounce and complaint reporting is a separate install. The events extra pulls in the pieces the webhook view needs:
pip install django-ses[events]You then wire a view into urls.py:
from django.urls import re_path
from django_ses.views import SESEventWebhookView
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [ ...
re_path(r'^ses/event-webhook/$', SESEventWebhookView.as_view(), name='handle-event-webhook'),
...
]The view handles bounce, complaint, send, delivery, open and click events, and it auto confirms subscription notifications.
The security note in the project documentation is the one to read twice. A valid SNS signature proves a message came from some AWS topic, not from your topic, so without AWS_SNS_EVENT_TRUSTED_TOPIC_ARNS anyone with an AWS account can post a signed notification from a topic of their own into your webhook. Set that setting to the ARNs of the topics you actually publish SES events to. Treat it as required configuration rather than a hardening option.
To produce the events, the AWS side needs an SNS topic and an SES destination configured under Configuration Sets, referenced by the AWS_SES_CONFIG setting.
Where plain Django already covers the same ground
The project makes an argument that deserves a direct answer. Amazon SES supports usernames and passwords, so if you configure it that way you do not need this package at all. The Django default email backend is capable of authenticating with SES and sending mail correctly.
That means the delta you are buying is specific: deliverability reports that are cumbersome to obtain over SMTP, the SNS event webhook, throttling against the live SES rate limit, and the SESv2 client with global endpoint support. If none of those matter to you, adding a dependency and an AWS credentials block in your settings buys you little.
The reverse is also worth checking before you commit. The project maintains a small internal example application, with example/settings.py, example/urls.py, example/middleware.py and example/views.py, and the README points new users at that example directory for more information. Reading it in your own project is the fastest way to see how the pieces fit.
Testing against SES is also possible without touching AWS. The repository ships a docker-compose.yml that runs a localstack container with the ses service, and its init script registers a domain identity so the local service accepts mail:
awslocal ses verify-domain-identity --domain "example.com" --region "eu-central-1"Version support is wide, but the maintainer is not using it in production
The package version is 4.8.2, and it requires Python 3.8 or newer with boto3 1.0.0 or newer. The Django classifiers list 3.2, 4.2, 5.2, 6.0 and 6.1, and the Python classifiers run from 3.8 through 3.14. Development status is marked as Production/Stable, and the licence is MIT.
The maintenance picture is more nuanced than that classifier. The last push was on 2026-10-13, and releases v4.8.1 and v4.8.2 both landed on 2026-10-12, with v4.8.0 on 2026-09-03. So the code is being changed recently. Yet the project states plainly that it is maintained but not actively used by the maintainer, and asks people who are actively using django-ses and willing to help to reach out through GitHub Issues.
That combination is common in infrastructure libraries and worth reading honestly: bug fixes still arrive, but the person most likely to own your incident is you. Plan on reading the changelog at CHANGES.md or the GitHub releases page before each upgrade, since release notes are the main channel for behavioural changes. The repository also carries a CVE directory, a pre-commit configuration and a tox.ini, which suggests some baseline hygiene around dependencies and test runs.
Editorial conclusion
Adopt django-ses if you run Django on AWS, need bounce and complaint visibility, or want to hand backends over to SES without touching application code. Do not adopt it for a single small app that already works with the default SMTP backend, and do not enable the SNS webhook without setting AWS_SNS_EVENT_TRUSTED_TOPIC_ARNS. Before committing, verify in your own AWS account that the SES region, endpoint and IAM role you intend to use match the settings block above, and confirm the SES sending quota on the account covers your peak volume.
Frequently asked questions
Does django-ses work with the MAILERS setting in Django 6.1 and later?
Yes. The project documents that MAILERS replaces EMAIL_BACKEND in Django 7.0, and the AWS settings are still read unchanged. Any SESBackend constructor argument can be supplied per mailer under OPTIONS.
How do I stop django-ses from throttling my sending rate?
Set AWS_SES_AUTO_THROTTLE to None. The default value of 0.5 applies a safety factor to the rate limit that the backend reads from the SES API.
Is it safe to expose the django-ses SNS event webhook without extra configuration?
No. A valid SNS signature only proves the message came from some AWS topic, not your topic, so anyone with an AWS account could post a signed notification from their own topic to your webhook. Set AWS_SNS_EVENT_TRUSTED_TOPIC_ARNS to the topic ARNs you publish SES events to.
Do I need to install anything extra to receive bounces and delivery notifications?
Yes. The events extra is required: pip install django-ses[events]. You also need an SNS topic in AWS and an SES destination in Configuration Sets, then route requests to SESEventWebhookView in your urls.py.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/django-ses-django-ses)Star history chart for your README
[](https://hysenlabs.com/projects/django-ses-django-ses)Was this useful?