Guide
How to send email with Django
Send transactional email from a Django view or task over the HTTP API.
In short
Create a Sendpository account, verify your domain, put your API key inSENDPOSITORY_API_KEY, and send with the code below - one HTTPS request per email, from server-side code only.1. Verify your sending domain
Add your domain in the dashboard and publish the three DNS records it shows - SPF and a bounce record on an sp. subdomain, and a DKIM key on your apex. Your existing records are never edited. Verification usually lands in a few minutes, and you can set up the code below while you wait.
2. Create a key
Create an API key with the sending permission. It is shown once, so put it straight into your secrets. Put SENDPOSITORY_API_KEY in your environment and read it in settings.py.
3. Install
pip install requests
4. Send an email
# settings.py
import os
SENDPOSITORY_API_KEY = os.environ["SENDPOSITORY_API_KEY"]
# emails.py
import requests
from django.conf import settings
def send_email(to, subject, html):
res = requests.post(
"https://api.sendpository.com/v1/emails",
headers={"Authorization": f"Bearer {settings.SENDPOSITORY_API_KEY}"},
json={"from": "Your App <hello@mail.yourdomain.com>", "to": [to], "subject": subject, "html": html},
timeout=10,
)
res.raise_for_status()
return res.json()["id"]A success returns { "id": "..." } - the message’s id for looking it up later. A failure returns { "error": { "type", "message" } } with a 4xx or 5xx status.
View or task?
Not the view, for anything triggered by a user. A send inside the request cycle holds the worker for the length of an HTTPS round trip, and Django's worker count is your concurrency limit.
Use Celery if you already run it, or `django.db.transaction.on_commit` for the simplest correct version: the email goes out only once the transaction that created the record has actually committed, so you never mail about a row that got rolled back.
Testing it
- Keep the key in settings read from the environment, not in `settings.py` - that file is committed.
- In tests, patch the send function rather than the HTTP layer. You are testing that your view decided to send, not that the API works.
Things to know
- Render templates with django.template.loader.render_to_string and pass the result as html.
- Send from a Celery task (or django-q) so a slow network never slows the request.
Questions
Should this go in the view or a Celery task?
A task, for anything a user triggers. A send inside the request cycle holds a worker for the length of an HTTPS round trip. If you have no queue, `transaction.on_commit` is the minimum correct version - it sends only after the transaction that created the record commits.
How do I send email with Django?
Verify your sending domain, create an API key, put it in an environment variable, and POST the message as JSON to https://api.sendpository.com/v1/emails from server-side Django code. The example on this page runs as written once the domain is verified.
Can I send email with Django without SMTP?
Yes. This is plain HTTPS, so Django needs no mail library and no SMTP port - which matters on hosts that block outbound 25 and 587. Failures come back as a status code on the call instead of as a bounce message hours later.
More guides
Send email with Python
Send email from Python with requests and the REST API.
Send email with Flask
Send email from a Flask route with the REST API.
Send your first email in minutes
6,000 emails a month (up to 200 a day), free forever. No card needed.