Skip to content
Email API for Django

Send email from Django with an email API

Send email from Django without an SMTP backend: one typed call in a view or a task, an id back, and delivery reported on a webhook.

Django — orders/views.pypip install rasket
# orders/views.py — pip install rasketimport os
from django.http import JsonResponsefrom rasket import Rasket

def send_shipping_email(request, order_id):    rasket = Rasket(        api_key=os.environ["RASKET_API_KEY"],        user_agent="acme-billing/1.0",    )
    result = rasket.emails.send(        {            "from": "Acme <orders@send.acme.example>",            "to": ["ronald.williams@example.com"],            "subject": "Your order has shipped",            "html": "<p>Order 1042 shipped today.</p>",        },        # Same key on every retry of this order.        idempotency_key=f"order-{order_id}",    )
    return JsonResponse({"id": result.body["id"]})

What sending from Django actually involves

To send email from Django you do not have to configure EMAIL_BACKEND and an SMTP host at all. Call the API from the view, the task or the signal handler that knows the mail should go out, and you get an id back you can store on the order. The rasket package is the typed client; the sample below is a view.

Last updated 2026-09-16. Every field, header and limit below is the one the emails reference carries.

What you get with the key

Five things that arrive with the first request rather than with a later plan.

  • Idempotent sends

    Put an Idempotency-Key on a send and repeat it as often as you like. For 24 hours the same key and payload return the first response instead of a second email, and a different payload under that key is refused rather than sent.

  • Signed webhooks you can replay

    Delivery, bounce, complaint, open and click posted to your endpoint with a timestamp and a signature, retried on failure and replayable from the dashboard.

  • Inbound mail on your own domain

    Receive at your domain on every plan. Each message arrives on a signed webhook with its headers, text and HTML, and its attachments behind signed links.

  • Templates with typed variables

    Publish a template, then send it by ID or alias with the values it declares. Every email records the exact version it was rendered from.

  • Broadcasts and automations

    Contacts with typed properties, topics people subscribe to, and per-contact workflows that wait, branch and send — through the same pipeline as the rest of your mail.

How to send email from Django

Six steps, in this order. Everything before the fifth is done once, and only the fifth is about the language you write in.

  1. 1

    Create an API key

    Open API keys in the dashboard and create one. It is shown once and stored hashed, so copy it then. Put it in your environment as RASKET_API_KEY and read it from there; a key committed to a repository is a key you have to rotate.

  2. 2

    Add a sending domain

    POST /domains with a domain you control. A subdomain such as send.acme.example is the usual choice, because it keeps this mail's reputation separate from the address your people write from. The response carries a records array.

  3. 3

    Publish the DNS records

    Every row of records goes into your DNS: the DKIM TXT record at rasket._domainkey, and the two records that give the domain its own return path. Nothing sends from the domain until they resolve.

  4. 4

    Verify the domain

    POST /domains/{domain_id}/verify. The domain comes back with a status on each record, and once it reads verified you can send from any address on it. An unverified sender is refused rather than quietly dropped.

  5. 5

    Send your first email from Django

    Post the message with the sample above. Carry an Idempotency-Key — yours or your queue's retry cannot then turn into a second email — and a User-Agent, which every request has to have. A 200 with an id means we have taken responsibility for the message.

  6. 6

    Subscribe a webhook and check its signature

    POST /webhooks with an https endpoint and the events you care about. Delivery, bounce, complaint, open and click arrive there with svix-id, svix-timestamp and svix-signature; verify against the raw body before anything parses it.

Verify webhooks in Django

Use request.body, which is the raw bytes, and read it before anything touches request.POST or json.loads. The view needs csrf_exempt, because the request comes from us rather than from a form on your site, and the signature is the authentication. Pass the bytes and the headers to the svix package's Webhook(secret).verify().

The five steps a verifier performs, in seven languages, are on the webhooks reference.

Read the long version

Sending email from Django: the full guide covers attachments, scheduling, suppressions and what to do when a message bounces.

Questions about Django

Do I need an SMTP server to send email from Django?

No. Django's EMAIL_BACKEND exists to speak SMTP for you, and calling an HTTPS API instead skips that layer entirely: no host, no port, no TLS settings, one key in the environment. Rasket has no SMTP relay, so a project that must keep using send_mail() is not a fit.

Where do I keep the API key in a Django project?

In the environment, read in settings.py as os.environ["RASKET_API_KEY"] alongside SECRET_KEY, so a deployment missing it fails at startup. Do not put it in a settings module you commit, and do not read it in a template.

Should I send inside the request or in a background task?

In a task, once you have more than a handful. The call is a network round trip, so doing it inside the view adds that latency to the response; Celery, django-q or any queue is the right place. Whichever you choose, give the send an Idempotency-Key so a retried task cannot send twice.

How do I stop a retry from sending the same email twice?

Derive idempotency_key from the row the mail is about — the order id, the invoice number — rather than generating one per attempt. For 24 hours the same key with the same payload returns the first response, so a task that is retried after a timeout is safe.

How do I verify a Rasket webhook in Django?

Write a csrf_exempt view, read request.body as the first statement, and pass it with request.headers to the svix package's verify(). Store the event and answer 200; answer 400 when verification raises. A view that reaches for request.POST first has already lost the bytes the signature covers.

Make your first send

Create a key, verify a domain, and post your first message from Django. The free plan does not expire.