Back to Blog
๐Ÿ
/4 min read/By SMTPfast Team

Send Transactional Emails from Python with SMTPfast

Use the requests library to send emails from any Python app. Includes FastAPI and Django integration patterns.

tutorialpythonfastapidjangogetting-started
Share:๐•in

Python + SMTPfast

10 lines
All you need to send an email from Python

SMTPfast's REST API works with any HTTP client. In Python, that means the requests library you probably already have installed.

Prerequisites

  • Python 3.8+
  • requests library (pip install requests)
  • An SMTPfast account with a verified domain
  • An API key from the SMTPfast dashboard

Send Your First Email

import requests
import os

def send_email(to, subject, html):
    response = requests.post(
        "https://smtpfa.st/api/v1/emails",
        headers={
            "Authorization": f"Bearer {os.environ['SMTPFAST_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "from": "[email protected]",
            "to": to,
            "subject": subject,
            "html": html,
        },
    )
    response.raise_for_status()
    return response.json()

result = send_email(
    "[email protected]",
    "Welcome!",
    "<h1>Welcome to our app!</h1>"
)
print(f"Email sent! ID: {result['id']}")

Run it:

SMTPFAST_API_KEY=sf_your_api_key python send_email.py

A Production-Ready Helper

Add retries, timeouts, and proper error handling:

import requests
import os
import time
from typing import Optional

SMTPFAST_URL = "https://smtpfa.st/api/v1/emails"


class SMTPfastError(Exception):
    def __init__(self, message, status_code=None):
        super().__init__(message)
        self.status_code = status_code


def send_email(
    to: str | list[str],
    subject: str,
    html: Optional[str] = None,
    text: Optional[str] = None,
    from_address: str = "[email protected]",
    reply_to: Optional[str] = None,
    tags: Optional[list[dict]] = None,
    retries: int = 3,
):
    payload = {
        "from": from_address,
        "to": to,
        "subject": subject,
    }
    if html:
        payload["html"] = html
    if text:
        payload["text"] = text
    if reply_to:
        payload["reply_to"] = reply_to
    if tags:
        payload["tags"] = tags

    headers = {
        "Authorization": f"Bearer {os.environ['SMTPFAST_API_KEY']}",
        "Content-Type": "application/json",
    }

    for attempt in range(1, retries + 1):
        try:
            response = requests.post(
                SMTPFAST_URL,
                headers=headers,
                json=payload,
                timeout=10,
            )

            if response.status_code == 429:
                retry_after = int(response.headers.get("Retry-After", "1"))
                time.sleep(retry_after)
                continue

            if not response.ok:
                error = response.json()
                raise SMTPfastError(error.get("message", "Unknown error"), response.status_code)

            return response.json()

        except requests.exceptions.RequestException as e:
            if attempt == retries:
                raise SMTPfastError(f"Request failed after {retries} attempts: {e}")
            time.sleep(2 ** attempt * 0.1)

    raise SMTPfastError("Max retries exceeded")

FastAPI Integration

Here is how to integrate SMTPfast into a FastAPI application:

from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel, EmailStr

app = FastAPI()

class InviteRequest(BaseModel):
    email: EmailStr
    team_name: str

def send_invite_email(email: str, team_name: str):
    """Send invite email in the background."""
    try:
        send_email(
            to=email,
            subject=f"You're invited to join {team_name}",
            html=f"<h1>Join {team_name}</h1><p>Click the link below to accept your invitation.</p>",
            tags=[{"name": "type", "value": "invite"}],
        )
    except SMTPfastError as e:
        # Log the error - do not crash the background task
        print(f"Failed to send invite email: {e}")

@app.post("/api/invite")
async def invite_user(invite: InviteRequest, background_tasks: BackgroundTasks):
    # Queue the email send as a background task
    background_tasks.add_task(send_invite_email, invite.email, invite.team_name)
    return {"status": "invite_sent"}

The key pattern: use BackgroundTasks so the API response returns immediately while the email sends in the background.

Django Integration

For Django, create a utility module and use it from your views:

# emails/utils.py
from django.conf import settings
import requests


def send_transactional_email(to, subject, html, tags=None):
    response = requests.post(
        "https://smtpfa.st/api/v1/emails",
        headers={
            "Authorization": f"Bearer {settings.SMTPFAST_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "from": settings.DEFAULT_FROM_EMAIL,
            "to": to,
            "subject": subject,
            "html": html,
            "tags": tags or [],
        },
        timeout=10,
    )
    response.raise_for_status()
    return response.json()

Add your API key to Django settings:

# settings.py
SMTPFAST_API_KEY = os.environ.get("SMTPFAST_API_KEY")
DEFAULT_FROM_EMAIL = "[email protected]"

Use it in a view:

# views.py
from django.http import JsonResponse
from .utils import send_transactional_email

def password_reset(request):
    user = request.user
    token = generate_reset_token(user)

    send_transactional_email(
        to=user.email,
        subject="Reset your password",
        html=f'<p>Click <a href="https://yourapp.com/reset/{token}">here</a> to reset your password.</p>',
        tags=[{"name": "type", "value": "password-reset"}],
    )

    return JsonResponse({"status": "reset_email_sent"})

For production Django apps, consider using Celery to send emails asynchronously:

# tasks.py
from celery import shared_task
from .utils import send_transactional_email

@shared_task(bind=True, max_retries=3)
def send_email_task(self, to, subject, html, tags=None):
    try:
        return send_transactional_email(to, subject, html, tags)
    except Exception as e:
        self.retry(exc=e, countdown=2 ** self.request.retries)
# views.py
from .tasks import send_email_task

def password_reset(request):
    user = request.user
    token = generate_reset_token(user)

    send_email_task.delay(
        to=user.email,
        subject="Reset your password",
        html=f'<p>Click <a href="https://yourapp.com/reset/{token}">here</a> to reset.</p>',
    )

    return JsonResponse({"status": "reset_email_sent"})

Check Delivery Status

def check_email_status(email_id):
    response = requests.get(
        f"https://smtpfa.st/api/v1/emails/{email_id}",
        headers={
            "Authorization": f"Bearer {os.environ['SMTPFAST_API_KEY']}",
        },
        timeout=10,
    )
    response.raise_for_status()
    data = response.json()
    print(f"Status: {data['last_event']}")
    return data

Key Takeaways

  1. Use the requests library - no special SDK needed
  2. Always set a timeout on HTTP requests
  3. Handle rate limits (429) with retry-after
  4. Send emails in the background (FastAPI BackgroundTasks, Django Celery)
  5. Use environment variables for API keys, never hardcode them

Ready to get started?

Start sending transactional email today. Free to start, no credit card required.

Get Started for Free