INTEGRATIONS · Published 2026-10-06 · Updated 2026-10-06 · SecureSMTP

Django

Settings for Django’s SMTP email backend, so send_mail() goes through SecureSMTP.

Django’s built-in SMTP email backend works with SecureSMTP. Change the settings and send_mail(), the password-reset emails and error emails to ADMINS all go through SecureSMTP. No package to install.

Setup

Every request needs your site’s API key. Get it when you add the site under Sites (/app/forms/sites) in the dashboard — see Sites and API keys. Keep it in an environment variable on the server:

Shell
export SECURESSMTP_API_KEY="qcs_live_..."
Never put the key in browser JavaScript, a mobile app or a public repository. Anyone who has it can send email as your site.

Settings

settings.py
# settings.py
import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.securessmtp.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "securessmtp"
EMAIL_HOST_PASSWORD = os.environ["SECURESSMTP_API_KEY"]
EMAIL_TIMEOUT = 30

DEFAULT_FROM_EMAIL = "Acme Store <[email protected]>"
SERVER_EMAIL = DEFAULT_FROM_EMAIL  # used for error emails to ADMINS

To use port 465 instead of 587:

settings.py
EMAIL_PORT = 465
EMAIL_USE_SSL = True   # instead of EMAIL_USE_TLS; do not set both
In relay mode (no verified domain) the From address is replaced: the email goes out from [email protected] with your From name, and the From address you set becomes the Reply-To. Verify your domain and the email goes out from your own address. See Domains and DNS.

Send an email

Python
from django.core.mail import send_mail

send_mail(
    subject="Your order has shipped",
    message="Your order is on its way.",
    from_email=None,  # uses DEFAULT_FROM_EMAIL
    recipient_list=["[email protected]"],
    html_message="<p>Your order is on its way.</p>",
    fail_silently=False,
)

With HTML and an attachment (up to 15 MB in total):

Python
from django.core.mail import EmailMultiAlternatives

email = EmailMultiAlternatives(
    subject="Invoice 1042",
    body="Your invoice is attached.",
    to=["[email protected]"],
)
email.attach_alternative("<p>Your invoice is attached.</p>", "text/html")
email.attach_file("invoices/invoice-1042.pdf")
email.send()

Handle errors

With fail_silently=False (the default), a refused email raises an smtplib exception that carries the SMTP reply code:

Python
import logging
import smtplib

from django.core.mail import send_mail

logger = logging.getLogger(__name__)

try:
    send_mail("Hello", "Hi there.", None, ["[email protected]"])
except smtplib.SMTPResponseException as err:
    # err.smtp_code is the SMTP reply, e.g. 450 rate limit, 550 rejected
    logger.error("Email rejected: %s %s", err.smtp_code, err.smtp_error)
except (smtplib.SMTPException, OSError) as err:
    logger.error("Email failed: %s", err)
SMTP replyMeaning
250Accepted.
535Login failed: the password is not a valid API key.
450 4.7.0Rate limit: more than 120 sends from this site in 60 seconds. Retry later.
550 5.7.0Monthly email limit reached.
550 5.7.1Rejected as spam, the site is disabled, or every recipient has unsubscribed.
550 5.1.1Every recipient is on the suppression list.
552 5.3.4Attachments are over 15 MB in total.
4xx 4.3.0Temporary failure. Retry later.

Or call the API

To get the message_id back or use API-only fields such as template, copy securessmtp.py from the Python guide into your app and call it from a view or a task:

views.py
# views.py: call the API instead of SMTP
from django.http import JsonResponse
from django.views.decorators.http import require_POST

from .securessmtp import SecureSMTPError, send_email


@require_POST
def notify(request):
    try:
        result = send_email({
            "to": "[email protected]",
            "subject": "Your order has shipped",
            "text": "Your order is on its way.",
            "from": {"name": "Acme Store"},
        })
    except SecureSMTPError as err:
        return JsonResponse({"ok": False, "reason": err.reason}, status=502)
    return JsonResponse({"ok": True, "message_id": result["message_id"]})
  • Check ok in the body, not only the HTTP status. Rate limit, monthly limit and send failures come back with HTTP 200.
  • There is no idempotency key. If a request times out, the email may already have been sent, and sending it again can deliver it twice.
  • Every reason is listed in the error catalogue.

Next