Use the official Lettermint Python SDK to send email from scripts, web applications, and workers. The package has synchronous and asynchronous clients. See Integrations for the SDK capability overview.
response = ( client.email .from_("John Doe <john@yourdomain.com>") .to("recipient@example.com") .subject("Hello from Lettermint") .text("This is a test email sent using the Lettermint Python SDK.") .send())print(f"Email sent with ID: {response['message_id']}")
3. Email features
Basic email
Send a simple text or HTML email:
Code
response = ( client.email .from_("John Doe <john@yourdomain.com>") .to("recipient@example.com") .subject("Your account is ready!") .html("<h1>Welcome!</h1><p>Thanks for signing up.</p>") .text("Welcome! Thanks for signing up.") .send())
This SDK example uses the existing singular tag. It has no deprecation date. Use the raw HTTP API to send structured tags until this SDK has a multi-tag method. See Tags documentation.
Route selection
Direct emails to specific routes within your project:
response = ( client.email .from_("notifications@yourdomain.com") .to("user@example.com") .subject("Order Confirmation") .html("<p>Your order has been confirmed.</p>") .idempotency_key("order-12345-confirmation") .send())
Use one unique key for each logical email. For example, combine the order ID and the email type. A retry with the same key does not send a duplicate email.
4. Send an order confirmation
A real transactional send usually pulls several of these features together. This example sends a receipt with an HTML and plain-text body, tags it for analytics, attaches the order and customer IDs as metadata, and uses an idempotency key so a retry never emails the customer twice:
Code
import osfrom lettermint import Lettermintclient = Lettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN"))def send_order_confirmation(order): return ( client.email .from_("Acme Store <orders@yourdomain.com>") .to(order["customer_email"]) .reply_to("support@yourdomain.com") .subject(f"Order {order['number']} confirmed") .html(f"<h1>Thanks for your order</h1><p>Order {order['number']} totalling {order['total']} is confirmed.</p>") .text(f"Thanks for your order. Order {order['number']} totalling {order['total']} is confirmed.") .tag("order-confirmation") .metadata({"order_id": order["id"], "customer_id": order["customer_id"]}) .idempotency_key(f"order-confirmation-{order['id']}") .route("transactional") .send() )
The metadata you attach here travels with every webhook event for the message, so a later delivery or bounce maps straight back to the order in your database.
5. Async support
The SDK provides an async client for use with asyncio:
Code
import asyncioimport osfrom lettermint import AsyncLettermintasync def send_email(): async with AsyncLettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN")) as client: response = await ( client.email .from_("John Doe <john@yourdomain.com>") .to("recipient@example.com") .subject("Hello from Lettermint") .text("This is a test email.") .send() ) print(f"Email sent with ID: {response['message_id']}")asyncio.run(send_email())
Use the async client in FastAPI, Starlette, or other async frameworks for better performance.
6. Client configuration
Customize the client with optional parameters:
Code
client = Lettermint( api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN"), base_url="https://api.lettermint.co/v1", # Custom API URL timeout=60.0, # Request timeout in seconds)
Use the client as a context manager for automatic resource cleanup:
Code
with Lettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN")) as client: response = ( client.email .from_("sender@yourdomain.com") .to("recipient@example.com") .subject("Test") .text("Hello!") .send() )
7. Response
Code
response = ( client.email .from_("John Doe <john@yourdomain.com>") .to("recipient@example.com") .subject("Test") .text("Hello!") .send())print(response["message_id"]) # Unique email ID
8. Error handling
Handle errors with specific exception types:
Code
from lettermint import Lettermintfrom lettermint.exceptions import ( ValidationError, ClientError, TimeoutError, HttpRequestError,)client = Lettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN"))try: response = ( client.email .from_("sender@yourdomain.com") .to("recipient@example.com") .subject("Test") .text("Hello!") .send() )except ValidationError as e: # 422 errors (invalid parameters, daily limit exceeded, etc.) print(f"Validation error: {e.error_type}")except ClientError as e: # 400 errors (bad request) print(f"Client error: {e}")except TimeoutError as e: # Request timed out print(f"Timeout: {e}")except HttpRequestError as e: # Other HTTP errors print(f"HTTP error {e.status_code}: {e}")
Track delivery, opens, and bounces
Sending is only half of a transactional setup. To see what happens after a message leaves your app, combine the SDK with Lettermint's platform features:
Email tracking records opens and clicks, with bot filtering so your metrics stay accurate.
Webhooks push delivery, bounce, and complaint events to your server in real time, so you can update order records or suppress bad addresses.
Test addresses let you trigger a hard or soft bounce on demand while you build and verify your webhook handler.
Tracking is configured per route, so turn it on for the route your app sends through, then read the results from your dashboard or your webhook endpoint.
FAQ
Does the SDK support async?
Yes. Import AsyncLettermint and use it with asyncio. It works well inside async frameworks such as FastAPI and Starlette, and the builder API is identical to the synchronous client.
How do I stop the same email being sent twice?
Add an idempotency key with .idempotency_key(), derived from a stable ID such as an order number. A repeated request with the same key returns the original result instead of sending again. See the idempotency documentation.
Is Lettermint email GDPR compliant and EU-hosted?
Yes. Lettermint runs exclusively on European infrastructure and processes email in line with GDPR, so transactional mail from your Python app is handled inside the EU.
How do I track opens, clicks, and bounces?
Enable tracking on your route and subscribe to webhooks to receive delivery, open, and bounce events. Use the test addresses to simulate bounces while you build your handler.