Send email from Python, Django, Flask and FastAPI with the official delivrie package, verify webhooks, or use Django's SMTP backend.
The official [delivrie](https://pypi.org/project/delivrie/) package covers the whole API. It has no dependencies, works on Python 3.10 and later, and comes in sync and async versions. (Already using the resend package? Set RESEND_API_URL=https://delivrie.io/api and it talks to Delivrie; see Migrate from Resend.)
pip install delivrie
Create an API key in API keys and set it as DELIVRIE_API_KEY.
from delivrie import Delivrie
client = Delivrie() # reads DELIVRIE_API_KEY
email = client.emails.send(
from_="Acme <hello@mail.acme.com>",
to="ada@example.com",
subject="Welcome to Acme",
html="<p>Thanks for signing up.</p>",
idempotency_key="welcome-42",
)
print(email["id"])
from is a Python keyword, so pass from_, or pass a dict exactly like the REST API: client.emails.send({"from": ..., "to": ..., ...}). A datetime works for scheduled_at, bytes for attachment content and a dict for tags.
With an idempotency_key, the SDK retries network errors, rate limits and server errors safely: a retry returns the original email instead of sending a second one. Without one, sends aren't retried. See Idempotency and retries.
Methods return the API's JSON as a dict and raise DelivrieError on failure:
from delivrie import DelivrieError
try:
client.emails.send(from_="Acme <hello@mail.acme.com>", to="ada@example.com", subject="Hi", text="Hello")
except DelivrieError as e:
print(e.status_code, e.name, e.message)
Branch on e.name (see Errors). status_code is 0 for network_error and timeout.
client.emails.send(
from_="Acme <billing@mail.acme.com>",
to="ada@example.com",
template={"id": "invoice", "variables": {"first_name": "Ada", "total": "$20.00"}},
attachments=[{"filename": "invoice.pdf", "content": open("invoice.pdf", "rb").read()}],
)
from delivrie import AsyncDelivrie, DelivrieError
from fastapi import FastAPI, HTTPException
app = FastAPI()
client = AsyncDelivrie()
@app.post("/signup")
async def signup(email: str):
try:
sent = await client.emails.send(
from_="Acme <hello@mail.acme.com>",
to=email,
subject="Welcome",
html="<p>Thanks for signing up.</p>",
idempotency_key=f"welcome-{email}",
)
except DelivrieError as e:
raise HTTPException(502, e.message)
return {"email_id": sent["id"]}
The simplest setup is Django's SMTP backend with the SMTP relay, so send_mail and password-reset emails go through Delivrie:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.delivrie.io"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "delivrie"
EMAIL_HOST_PASSWORD = os.environ["DELIVRIE_API_KEY"]
DEFAULT_FROM_EMAIL = "Acme <hello@mail.acme.com>"
For templates, scheduling or tags, call the SDK from your views or tasks instead.
from delivrie import Delivrie, DelivrieError
from flask import Flask, request
app = Flask(__name__)
client = Delivrie()
@app.post("/contact")
def contact():
try:
client.emails.send(
from_="Website <forms@mail.acme.com>",
to="team@acme.com",
reply_to=request.form["email"],
subject="New contact form message",
text=request.form["message"],
)
except DelivrieError as e:
return e.message, 502
return "Sent", 200
Pass the raw body, before parsing it:
import os
from delivrie import verify_webhook, WebhookVerificationError
@app.post("/webhooks/delivrie")
def delivrie_webhook():
try:
event = verify_webhook(request.get_data(), request.headers, os.environ["DELIVRIE_WEBHOOK_SECRET"])
except WebhookVerificationError:
return "", 400
if event["type"] == "email.bounced":
...
return "", 204
In Django pass request.body and request.headers; in FastAPI, await request.body() and request.headers.
The client uses the same names as the Node.js SDK, in snake_case: client.domains, client.api_keys, client.webhooks, client.contacts, client.segments, client.broadcasts, client.templates, client.suppressions, client.events, client.automations, client.inbox.threads, client.metrics, client.logs and client.usage. For anything else, client.request("GET", "/path") calls the API directly.
The API is a single JSON request, so requests or httpx work too:
import os, requests
res = requests.post(
"https://delivrie.io/api/emails",
headers={"Authorization": f"Bearer {os.environ['DELIVRIE_API_KEY']}", "Idempotency-Key": "welcome-42"},
json={"from": "Acme <hello@mail.acme.com>", "to": "ada@example.com", "subject": "Welcome", "html": "<p>Hi</p>"},
timeout=30,
)
res.raise_for_status()
print(res.json()["id"])