A Django email backend for Cloudflare Email Service (public beta, April 2026).
Send transactional email through Cloudflare's first-party email API with full support for HTML, attachments, cc/bcc, reply-to, and custom headers.
pip install django-cloudflare-emailOr with uv:
uv add django-cloudflare-emailRequirements: Python 3.11+, Django 4.2+
Add the following to your Django settings:
# settings.py
EMAIL_BACKEND = "django_cloudflare_email.backend.CloudflareEmailBackend"
# Your Cloudflare account ID (found in the Cloudflare dashboard)
CLOUDFLARE_ACCOUNT_ID = "your-account-id"
# A Cloudflare API token with Email Send permissions
CLOUDFLARE_API_TOKEN = "your-api-token"- Log in to the Cloudflare dashboard
- Find your Account ID in the right sidebar on any zone page
- Create an API Token at dash.cloudflare.com/profile/api-tokens with the
Email Sendpermission
Once configured, use Django's standard email API:
from django.core.mail import send_mail, EmailMultiAlternatives
# Plain text
send_mail(
subject="Hello from Cloudflare",
message="Plain text body",
from_email="noreply@example.com",
recipient_list=["user@example.com"],
)
# HTML email with plain-text fallback
msg = EmailMultiAlternatives(
subject="Welcome!",
body="Welcome to our service.", # plain-text fallback
from_email="welcome@example.com",
to=["newuser@example.com"],
)
msg.attach_alternative("<p>Welcome to our service.</p>", "text/html")
msg.send()From/To addresses support RFC 5322 display names:
from django.core.mail import EmailMessage
msg = EmailMessage(
subject="Hello",
body="Body text",
from_email="My App <noreply@example.com>",
to=["Alice Smith <alice@example.com>"],
reply_to=["Support Team <support@example.com>"],
)
msg.send()from django.core.mail import EmailMessage
msg = EmailMessage(
subject="Report attached",
body="Please find the report attached.",
from_email="reports@example.com",
to=["manager@example.com"],
)
# Attach bytes directly
with open("report.pdf", "rb") as f:
msg.attach("report.pdf", f.read(), "application/pdf")
msg.send()msg = EmailMessage(...)
msg.extra_headers = {
"X-Campaign-Id": "summer-2026",
"X-Mailer": "MyApp/1.0",
}
msg.send()All errors are subclasses of CloudflareEmailError:
from django_cloudflare_email.exceptions import (
CloudflareEmailError,
CloudflareEmailThrottledError,
CloudflareEmailDisabledError,
CloudflareEmailInvalidAddressError,
)
try:
msg.send()
except CloudflareEmailThrottledError:
# HTTP 429 or CF error code 10004 — back off and retry
time.sleep(60)
msg.send()
except CloudflareEmailDisabledError:
# CF error code 10203 — sending is disabled for this account
logger.error("Cloudflare Email sending is disabled")
except CloudflareEmailInvalidAddressError as e:
# CF error code 10200 — invalid recipient or sender address
logger.error("Invalid address: %s", e)
except CloudflareEmailError as e:
# Catch-all for any other Cloudflare error
logger.error("Email send failed (code %s): %s", e.error_code, e)| Exception | CF Code | HTTP | Meaning |
|---|---|---|---|
CloudflareEmailSchemaError |
10001 | 200 | Request body failed schema validation |
CloudflareEmailInternalError |
10002 | 5xx | Cloudflare internal error |
CloudflareEmailThrottledError |
10004 | 429 | Rate limited |
CloudflareEmailInvalidAddressError |
10200 | 200 | Invalid email address |
CloudflareEmailContentLengthError |
10201 | 200 | Missing or invalid content length |
CloudflareEmailTooLargeError |
10202 | 200 | Message too large |
CloudflareEmailDisabledError |
10203 | 200 | Sending disabled for account |
Django's fail_silently flag is respected. When True, errors are logged but not raised:
msg.send(fail_silently=True)Or configure at the backend level via connection:
from django.core.mail import get_connection
connection = get_connection(fail_silently=True)
msg = EmailMessage(..., connection=connection)
msg.send()git clone https://github.com/yudame/django-cloudflare-email
cd django-cloudflare-email
uv sync --all-extras
pytest tests/ -vMIT. See LICENSE.