> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sendbaze.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send emails with Python

> Learn how to send your first email using the Sendbaze Python SDK, from a script, Django, Flask or FastAPI.

## Prerequisites

Before you start, you need:

* A Sendbaze [API key](https://docs.sendbaze.com/get-started/create-api-key.md)
* A [verified domain](https://docs.sendbaze.com/get-started/add-domain.md). A test key works without one, and so does the [shared test address](https://docs.sendbaze.com/get-started/test-without-a-domain.md) for emails to yourself.
* Python 3.9 or later

## Guide

**1. Install**

The SDK has no dependencies beyond the standard library.

```bash pip theme={"dark"}
pip install sendbaze
```

```bash poetry theme={"dark"}
poetry add sendbaze
```

```bash uv theme={"dark"}
uv add sendbaze
```

**2. Set your API key**

Store the key in an environment variable in your `.env` file. The client reads `SENDBAZE_API_KEY` by default.

```bash .env theme={"dark"}
SENDBAZE_API_KEY=sk_live_xxxxxxxx
```

**3. Send an email**

```python send.py theme={"dark"}
from sendbaze import Sendbaze

sendbaze = Sendbaze()

email = sendbaze.emails.send(
    from_="Yourshop <orders@yourshop.co.za>",
    to="thandi@example.com",
    subject="Your order #1042 has shipped",
    html="<p>Tracking: <b>CG123456789ZA</b></p>",
)
print(email.id)  # em_01j9...
```

`from_` stands in for the reserved word `from`; `class_` does the same for `class`. Snake\_case names such as `reply_to` and `template_id` are converted to the API's camelCase.

The call returns as soon as the email is queued. Delivery is reported by [webhook](https://docs.sendbaze.com/learn/webhooks/overview.md) and in the dashboard **Logs**.

**4. Try it**

Run the script and open the dashboard **Logs**. A live key delivers within seconds; a test key shows the message with a test badge and sends nothing.

## Examples

### Handle errors

Every non-2xx response raises `SendbazeError` with a stable `code` that matches the [errors reference](https://docs.sendbaze.com/errors.md). Network failures raise `SendbazeConnectionError`, which is safe to retry with the same idempotency key.

```python theme={"dark"}
from sendbaze import Sendbaze, SendbazeError, SendbazeConnectionError

sendbaze = Sendbaze()

try:
    sendbaze.emails.send(
        from_="orders@yourshop.co.za",
        to="thandi@example.com",
        subject="Hi",
        text="Hello",
        idempotency_key="order-1042-shipped",
    )
except SendbazeError as error:
    print(error.status, error.code, error.fields, error.request_id)
except SendbazeConnectionError:
    ...  # retry later with the same idempotency_key
```

### Send with a template

```python theme={"dark"}
sendbaze.emails.send(
    from_="orders@yourshop.co.za",
    to="thandi@example.com",
    template_id="tpl_01j9x7k2r4m8q3v5n6b7c8d9e0",
    variables={"name": "Thandi", "order_id": 1042, "eta": "Friday"},
)
```

### Attach a file

```python theme={"dark"}
import base64

with open("invoice-1042.pdf", "rb") as f:
    content = base64.b64encode(f.read()).decode()

sendbaze.emails.send(
    from_="orders@yourshop.co.za",
    to="thandi@example.com",
    subject="Invoice 1042",
    html="<p>Your invoice is attached.</p>",
    attachments=[{"filename": "invoice-1042.pdf", "content_type": "application/pdf", "content": content}],
)
```

### Look up and list messages

```python theme={"dark"}
message = sendbaze.emails.get("em_01j9...")
print(message.status)  # "queued" | "sent" | "delivered" | "bounced" | ...
for event in message.events:
    print(event.type, event.occurredAt)

page = sendbaze.emails.list(status="bounced", limit=100)
for email in page.data:
    print(email.id, email.to)
```

### Django

Call the client from a view, a signal handler or a Celery task. Keep the key in the environment, never in the repo.

```python views.py theme={"dark"}
from django.http import JsonResponse
from sendbaze import Sendbaze

sendbaze = Sendbaze()

def resend_receipt(request, order_id):
    order = request.user.orders.get(pk=order_id)
    email = sendbaze.emails.send(
        from_="orders@yourshop.co.za",
        to=request.user.email,
        subject=f"Receipt for order #{order.pk}",
        text=order.receipt_text(),
        tags=["receipt"],
        idempotency_key=f"receipt-{order.pk}",
    )
    return JsonResponse({"id": email["id"]})
```

### Verify a webhook (Flask)

```python theme={"dark"}
from flask import Flask, abort, request
from sendbaze import verify_webhook_signature, SENDBAZE_SIGNATURE_HEADER

app = Flask(__name__)

@app.post("/webhooks/sendbaze")
def sendbaze_webhook():
    if not verify_webhook_signature(request.get_data(), request.headers.get(SENDBAZE_SIGNATURE_HEADER), SECRET):
        abort(400)
    event = request.get_json()
    if event["type"] == "email.bounced":
        ...  # mark event["data"]["to"] as undeliverable
    return {"received": True}
```

## Without the SDK

The API is plain JSON over HTTPS, so `requests` or `httpx` work too:

```python theme={"dark"}
import os, requests

response = requests.post(
    "https://api.sendbaze.com/v1/emails",
    headers={"Authorization": f"Bearer {os.environ['SENDBAZE_API_KEY']}"},
    json={"from": "orders@yourshop.co.za", "to": "thandi@example.com", "subject": "Hi", "text": "Hello"},
    timeout=30,
)
response.raise_for_status()
print(response.json()["id"])
```

> [card] [SDK reference](https://docs.sendbaze.com/resources/sdks.md)
>
> Every resource the SDK covers: emails, templates, domains, suppressions, webhooks, SMS and usage.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.