Send email in Django with an email API

How to send transactional email from Django — requests/httpx to Notify, no SMTP backend required.

Send transactional email from Django with an HTTPS call from a view, signal handler, or Celery task. Keep the API key in settings/env — never in templates. You do not need Django’s SMTP email backend for this path.

Prerequisites

  • API key — authenticate every request with the x-api-key header
  • Verified domain — required for production sends from a custom from address
  • Django 4.2+ and Python 3.10+
  • requests or httpx
pip install requests
export NOTIFY_API_KEY=your_api_key_here

Settings

# settings.py
import os

NOTIFY_API_KEY = os.environ["NOTIFY_API_KEY"]
NOTIFY_FROM = os.environ.get("NOTIFY_FROM", "noreply@your-verified-domain.com")
NOTIFY_SEND_URL = "https://notify.cx/api/email/send"

Helper module

# mailer/notify.py
from django.conf import settings
import requests


class NotifyError(Exception):
    pass


def send_email(*, to: str, subject: str, message: str, from_addr: str | None = None) -> dict:
    response = requests.post(
        settings.NOTIFY_SEND_URL,
        headers={
            "Content-Type": "application/json",
            "x-api-key": settings.NOTIFY_API_KEY,
        },
        json={
            "from": from_addr or settings.NOTIFY_FROM,
            "to": to,
            "subject": subject,
            "message": message,  # plain text or HTML
        },
        timeout=15,
    )
    if not response.ok:
        raise NotifyError(f"{response.status_code}: {response.text}")
    return response.json()

Call from a view

# accounts/views.py
from django.http import JsonResponse, HttpRequest
from django.views.decorators.http import require_POST
from mailer.notify import send_email, NotifyError


@require_POST
def send_welcome(request: HttpRequest):
    email = request.POST.get("email")
    if not email:
        return JsonResponse({"error": "email required"}, status=400)

    try:
        send_email(
            to=email,
            subject="Welcome",
            message="<h1>Welcome</h1><p>Thanks for joining.</p>",
        )
    except NotifyError as exc:
        return JsonResponse({"error": str(exc)}, status=502)

    return JsonResponse({"ok": True})

Under load, push sends to Celery (or similar) so the HTTP response does not wait on Notify. Prefer explicit calls from the signup path over broad post_save signals.

Render HTML with render_to_string('emails/welcome.html', context) and pass the result as message.


Where to next?