Python Integration
Send emails from Python applications using smtplib, the NorthRelay SDK, Django, Flask, or FastAPI
Python Integration
This guide covers sending email from Python using the standard library (smtplib), the northrelay SDK, and popular web frameworks.
Using smtplib (Standard Library)
No additional packages needed. Python's built-in smtplib supports SMTP with STARTTLS.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_email(to: str, subject: str, text: str, html: str | None = None):
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = "notifications@yourdomain.com"
msg["To"] = to
msg.attach(MIMEText(text, "plain"))
if html:
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP("smtp.north-pipe.ca", 587) as server:
server.starttls()
server.login("your-email@example.com", "nr_live_xxxxxxxxxxxx")
server.send_message(msg)
print(f"Email sent to {to}")
# Usage
send_email(
to="recipient@example.com",
subject="Hello from NorthRelay!",
text="This is a test email.",
html="<h1>Hello!</h1><p>This is a test email.</p>",
)With Environment Variables
import os
import smtplib
from email.mime.text import MIMEText
SMTP_HOST = "smtp.north-pipe.ca"
SMTP_PORT = 587
SMTP_USER = os.environ["NORTHRELAY_USER"]
SMTP_PASS = os.environ["NORTHRELAY_API_KEY"]
FROM_ADDRESS = os.environ.get("NORTHRELAY_FROM", SMTP_USER)
def send_email(to: str, subject: str, body: str):
msg = MIMEText(body)
msg["Subject"] = subject
msg["From"] = FROM_ADDRESS
msg["To"] = to
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls()
server.login(SMTP_USER, SMTP_PASS)
server.send_message(msg)Using the NorthRelay SDK
Installation
pip install northrelayThe SDK is async: every method is a coroutine. Use it from async code (FastAPI, Starlette, aiohttp) or wrap it with asyncio.run.
Basic Usage
import asyncio
import os
from northrelay import NorthRelay, SendEmailRequest
async def main() -> None:
async with NorthRelay(api_key=os.environ["NORTHRELAY_API_KEY"]) as client:
result = await client.emails.send(
SendEmailRequest(
from_={"email": "notifications@yourdomain.com", "name": "My App"},
to=[{"email": "recipient@example.com"}],
content={
"subject": "Hello from NorthRelay!",
"text": "This is a test email.",
"html": "<h1>Hello!</h1><p>This is a test email.</p>",
},
)
)
print(f"Email queued: {result.message_id} (status: {result.status})")
asyncio.run(main())async with closes the connection pool on exit; a long-lived client can instead call await client.close() at shutdown.
Sending a Template
result = await client.emails.send_template(
"template_id",
[{"email": "alice@example.com", "name": "Alice"}],
{"first_name": "Alice", "order_id": "1234"},
from_={"email": "orders@yourdomain.com"},
)Always pass from_ with an address on a domain you have verified.
Error Handling
from northrelay import NorthRelayError, RateLimitError, ValidationError
try:
result = await client.emails.send(
SendEmailRequest(
from_={"email": "notifications@yourdomain.com"},
to=[{"email": "recipient@example.com"}],
content={"subject": "Test", "text": "Test email"},
)
)
print(f"Queued: {result.message_id}")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds.")
except ValidationError as e:
print(f"Invalid request: {e.message} {e.errors}")
except NorthRelayError as e:
print(f"API error [{e.details.get('code')}] ({e.status_code}): {e.message}")
if e.details.get("fix_action"):
print(f"Suggested fix: {e.details['fix_action']}")Every exception extends NorthRelayError and carries message, status_code and details (the API's code, fix_action and docs_url). See the Python SDK for the full list.
Using Django
Django's built-in email framework supports SMTP out of the box.
settings.py
# Email configuration
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.north-pipe.ca"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.environ["NORTHRELAY_USER"]
EMAIL_HOST_PASSWORD = os.environ["NORTHRELAY_API_KEY"]
DEFAULT_FROM_EMAIL = "notifications@yourdomain.com"Sending Email
from django.core.mail import send_mail, EmailMultiAlternatives
# Simple text email
send_mail(
subject="Welcome!",
message="Thanks for signing up.",
from_email=None, # Uses DEFAULT_FROM_EMAIL
recipient_list=["recipient@example.com"],
)
# HTML email with plain text fallback
email = EmailMultiAlternatives(
subject="Welcome!",
body="Thanks for signing up.",
from_email=None,
to=["recipient@example.com"],
)
email.attach_alternative("<h1>Welcome!</h1><p>Thanks for signing up.</p>", "text/html")
email.send()Testing in Django
# settings.py — for local development, log emails to console
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"Using Flask-Mail
Installation
pip install Flask-MailConfiguration
from flask import Flask
from flask_mail import Mail, Message
app = Flask(__name__)
app.config.update(
MAIL_SERVER="smtp.north-pipe.ca",
MAIL_PORT=587,
MAIL_USE_TLS=True,
MAIL_USERNAME=os.environ["NORTHRELAY_USER"],
MAIL_PASSWORD=os.environ["NORTHRELAY_API_KEY"],
MAIL_DEFAULT_SENDER="notifications@yourdomain.com",
)
mail = Mail(app)
@app.route("/send")
def send():
msg = Message(
subject="Hello from Flask!",
recipients=["recipient@example.com"],
body="This is a test email from Flask.",
html="<p>This is a test email from <strong>Flask</strong>.</p>",
)
mail.send(msg)
return "Email sent!"Using FastAPI with the SDK
Installation
pip install fastapi uvicorn northrelayApplication
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
from northrelay import NorthRelay, NorthRelayError, SendEmailRequest
email_client = NorthRelay(api_key=os.environ["NORTHRELAY_API_KEY"])
@asynccontextmanager
async def lifespan(app: FastAPI):
yield
await email_client.close()
app = FastAPI(lifespan=lifespan)
class EmailRequest(BaseModel):
to: EmailStr
subject: str
body: str
@app.post("/send-email")
async def send_email(request: EmailRequest):
try:
result = await email_client.emails.send(
SendEmailRequest(
from_={"email": os.environ["NORTHRELAY_FROM"]},
to=[{"email": request.to}],
content={"subject": request.subject, "text": request.body},
)
)
return {"success": True, "messageId": result.message_id}
except NorthRelayError as e:
raise HTTPException(status_code=e.status_code or 502, detail=e.details.get("fix_action") or e.message)Run with:
uvicorn app:app --reloadEnvironment Variables
Store credentials in environment variables. Never hardcode API keys.
# .env
NORTHRELAY_USER=your-email@example.com
NORTHRELAY_API_KEY=nr_live_xxxxxxxxxxxx
NORTHRELAY_FROM=notifications@yourdomain.comLoad with python-dotenv:
from dotenv import load_dotenv
load_dotenv()Related Guides
- SMTP Configuration — Full SMTP settings reference
- DNS Setup Guide — Configure SPF, DKIM, and DMARC
- Troubleshooting — Common issues and fixes