Send emails with Python
pip install mailstein
Every call returns (data, error) rather than raising, so the failure case is
impossible to forget.
import os
from mailstein import Mailstein
mailstein = Mailstein(os.environ["MAILSTEIN_API_KEY"])
data, error = mailstein.emails.send({
"from": "hello@yourdomain.com",
"to": "someone@example.com",
"subject": "Welcome",
"html": "<p>Thanks for signing up.</p>",
})
if error:
raise RuntimeError(error["message"])
print(data["emailId"])
FastAPI
from fastapi import BackgroundTasks, FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
app = FastAPI()
class Signup(BaseModel):
email: EmailStr
def send_welcome(email: str) -> None:
_, error = mailstein.emails.send({
"from": "hello@yourdomain.com",
"to": email,
"subject": "Welcome",
"html": "<p>Thanks for signing up.</p>",
})
if error:
# Log it; do not raise. Nobody is waiting on this, and an exception in
# a background task is a signup that already succeeded looking broken.
print("send failed:", error)
@app.post("/signup")
def signup(body: Signup, background: BackgroundTasks):
background.add_task(send_welcome, body.email)
return {"ok": True}
Sending in the background is the point. The SDK is synchronous, so calling it inline blocks the request for as long as the send takes.
Django
# views.py
from django.http import JsonResponse
def signup(request):
data, error = mailstein.emails.send({
"from": "hello@yourdomain.com",
"to": request.POST["email"],
"subject": "Welcome",
"html": "<p>Thanks for signing up.</p>",
})
if error:
return JsonResponse({"error": "Could not send"}, status=502)
return JsonResponse({"id": data["emailId"]})
For anything beyond a handful of messages, put the send in Celery rather than
the request. Or use SMTP, which lets Django's own
send_mail work unchanged.
Something here wrong or missing? It is generated from the running API —
tell us and we will fix the source.