> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.brevo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.brevo.com/_mcp/server.

# Python SDK

## Overview

The Brevo Python SDK (`brevo-python`) is a fully typed client library for the Brevo API. It provides:

* A unified `Brevo` client with namespaced service clients
* Native async support via `AsyncBrevo`
* Pydantic-based typed models with full type annotations
* Automatic retries with exponential backoff
* Raw response access with headers and status codes
* Custom `httpx` client support for proxies and mTLS

[![PyPI](https://img.shields.io/pypi/dm/brevo-python)](https://pypi.org/project/brevo-python/)

Version 5.0 introduces breaking changes versus v4.x. v4.x remains supported and continues to receive wire-compatibility fixes — see the [changelog](/guides/python/changelog) for the list of changes and the README for migration steps.

## Requirements

* Python 3.8+
* `httpx` >= 0.21.2
* `pydantic` >= 1.9.2
* `typing_extensions` >= 4.0.0

## Installation

```bash
pip install brevo-python
```

## Quick start

Initialize the client and send your first email:

```python title="quick_start.py"
from brevo import Brevo
from brevo.transactional_emails import (
    SendTransacEmailRequestSender,
    SendTransacEmailRequestToItem,
)

client = Brevo(api_key="your-api-key")

result = client.transactional_emails.send_transac_email(
    subject="Hello from Brevo!",
    html_content="<html><body><p>Hello,</p><p>This is my first transactional email.</p></body></html>",
    sender=SendTransacEmailRequestSender(
        name="Alex from Brevo",
        email="hello@brevo.com",
    ),
    to=[
        SendTransacEmailRequestToItem(
            email="johndoe@example.com",
            name="John Doe",
        )
    ],
)

print("Email sent. Message ID:", result.message_id)
```

## Configuration

Pass keyword arguments to the constructor to configure the client:

```python title="configuration.py"
from brevo import Brevo

client = Brevo(
    api_key="your-api-key",
    timeout=30.0,
)
```

### Constructor parameters

| Parameter          | Type                  | Default  | Description                                        |
| ------------------ | --------------------- | -------- | -------------------------------------------------- |
| `api_key`          | `str`                 | Required | Your Brevo API key                                 |
| `timeout`          | `float`               | `60.0`   | Default request timeout in seconds                 |
| `base_url`         | `str`                 | `None`   | Override the default API base URL                  |
| `follow_redirects` | `bool`                | `True`   | Follow HTTP redirects                              |
| `httpx_client`     | `httpx.Client`        | `None`   | Custom httpx client instance                       |
| `headers`          | `dict`                | `None`   | Additional default headers sent with every request |
| `logging`          | `LogConfig \| Logger` | `None`   | Logging configuration (see [Logging](#logging))    |

## Async client

Use `AsyncBrevo` for non-blocking calls. Pass `httpx.AsyncClient` instead of `httpx.Client` when providing a custom HTTP client:

```python title="async_client.py"
import asyncio
from brevo import AsyncBrevo
from brevo.transactional_emails import (
    SendTransacEmailRequestSender,
    SendTransacEmailRequestToItem,
)

client = AsyncBrevo(api_key="your-api-key")

async def main() -> None:
    result = await client.transactional_emails.send_transac_email(
        subject="Hello from Brevo!",
        html_content="<html><body><p>Hello!</p></body></html>",
        sender=SendTransacEmailRequestSender(
            name="Alex from Brevo",
            email="hello@brevo.com",
        ),
        to=[
            SendTransacEmailRequestToItem(
                email="johndoe@example.com",
                name="John Doe",
            )
        ],
    )
    print("Email sent. Message ID:", result.message_id)

asyncio.run(main())
```

## Error handling

The SDK raises `ApiError` (or a typed subclass) for non-2xx HTTP responses:

```python title="error_handling.py"
from brevo import Brevo
from brevo.core.api_error import ApiError

client = Brevo(api_key="your-api-key")

try:
    client.transactional_emails.send_transac_email(...)
except ApiError as e:
    print(e.status_code)
    print(e.body)
```

### Error classes

| Status code | Class                       |
| ----------- | --------------------------- |
| `400`       | `BadRequestError`           |
| `401`       | `UnauthorizedError`         |
| `402`       | `PaymentRequiredError`      |
| `403`       | `ForbiddenError`            |
| `404`       | `NotFoundError`             |
| `405`       | `MethodNotAllowedError`     |
| `409`       | `ConflictError`             |
| `412`       | `PreconditionFailedError`   |
| `415`       | `UnsupportedMediaTypeError` |
| `422`       | `UnprocessableEntityError`  |
| `424`       | `FailedDependencyError`     |
| `429`       | `TooManyRequestsError`      |
| `500`       | `InternalServerError`       |

All `ApiError` instances expose:

* `status_code` — HTTP status code
* `body` — Parsed response body
* `headers` — Response headers

## Retries

Automatic retries with exponential backoff are enabled by default (2 retries). Configure at the client or request level:

```python title="retries.py"
from brevo import Brevo

# Client-level (not directly supported — use request_options per request)
client = Brevo(api_key="your-api-key")

# Request-level
client.transactional_emails.send_transac_email(
    ...,
    request_options={"max_retries": 3},
)
```

### Retry behavior

* **Retryable status codes**: `408`, `429`, `5xx`
* **Backoff**: Exponential with jitter
* **Disable**: Set `max_retries: 0` in `request_options`

## Timeouts

Default timeout is 60 seconds. Configure at the client or request level:

```python title="timeouts.py"
from brevo import Brevo

# Client-level
client = Brevo(
    api_key="your-api-key",
    timeout=30.0,
)

# Request-level
client.transactional_emails.send_transac_email(
    ...,
    request_options={"timeout_in_seconds": 10},
)
```

### Recommended timeout values

| Use case                | Timeout            |
| ----------------------- | ------------------ |
| Standard API calls      | `30–60s` (default) |
| Quick operations        | `10–15s`           |
| Bulk operations         | `120–300s`         |
| Real-time / low-latency | `5–10s`            |

## Request options

All service methods accept a `request_options` dict as the final keyword argument:

| Option                        | Type   | Description                               |
| ----------------------------- | ------ | ----------------------------------------- |
| `timeout_in_seconds`          | `int`  | Override timeout for this request         |
| `max_retries`                 | `int`  | Override max retries for this request     |
| `additional_headers`          | `dict` | Merge additional headers into the request |
| `additional_query_parameters` | `dict` | Add query parameters to the request       |
| `additional_body_parameters`  | `dict` | Add body parameters to the request        |

```python title="request_options.py"
client.transactional_emails.send_transac_email(
    ...,
    request_options={
        "timeout_in_seconds": 10,
        "max_retries": 1,
        "additional_headers": {"X-Custom-Header": "custom-value"},
    },
)
```

## Raw response access

Access response headers and status code via `.with_raw_response`:

```python title="raw_response.py"
from brevo import Brevo

client = Brevo(api_key="your-api-key")

response = client.transactional_emails.with_raw_response.send_transac_email(...)

print(response.status_code)
print(response.headers)
print(response.data)
```

## Logging

The SDK has a built-in, opt-in logger you can plug your own implementation into. Pass a `LogConfig` dict (or a pre-built `Logger`) via the `logging` constructor option.

By default the SDK is **silent**: nothing is logged unless you set `silent=False`. This keeps integration changes from accidentally producing log volume in production.

```python title="logging_basic.py"
from brevo import Brevo
from brevo.core.logging import ConsoleLogger

client = Brevo(
    api_key="your-api-key",
    logging={
        "level": "debug",          # "debug" | "info" | "warn" | "error"
        "logger": ConsoleLogger(), # built-in; writes to stdlib `logging` under the "fern" logger
        "silent": False,           # required to actually emit logs
    },
)
```

### `LogConfig` fields

| Field    | Type                                     | Default           | Description                                                             |
| -------- | ---------------------------------------- | ----------------- | ----------------------------------------------------------------------- |
| `level`  | `"debug" \| "info" \| "warn" \| "error"` | `"info"`          | Minimum level that gets forwarded to the logger                         |
| `logger` | `ILogger`                                | `ConsoleLogger()` | Logger implementation — any object conforming to the `ILogger` protocol |
| `silent` | `bool`                                   | `True`            | When `True`, all logging is suppressed regardless of `level`            |

### Custom logger

Implement the `ILogger` protocol (a `typing.Protocol` with `debug`, `info`, `warn`, `error` methods) to forward to any logging library — Python's stdlib `logging`, `structlog`, `loguru`, or your own sink.

```python title="custom_logger.py"
import logging
from brevo import Brevo
from brevo.core.logging import ILogger

logging.basicConfig(level=logging.DEBUG)

class StdlibLogger(ILogger):
    def __init__(self, name: str = "brevo") -> None:
        self._log = logging.getLogger(name)

    def debug(self, message: str, **kwargs) -> None: self._log.debug(message, extra=kwargs)
    def info(self,  message: str, **kwargs) -> None: self._log.info(message,  extra=kwargs)
    def warn(self,  message: str, **kwargs) -> None: self._log.warning(message, extra=kwargs)
    def error(self, message: str, **kwargs) -> None: self._log.error(message, extra=kwargs)

client = Brevo(
    api_key="your-api-key",
    logging={"level": "debug", "logger": StdlibLogger(), "silent": False},
)
```

### Integrations

#### structlog

```python
import structlog
from brevo import Brevo
from brevo.core.logging import ILogger

class StructlogLogger(ILogger):
    def __init__(self) -> None:
        self._log = structlog.get_logger("brevo")

    def debug(self, message, **kwargs): self._log.debug(message, **kwargs)
    def info(self,  message, **kwargs): self._log.info(message,  **kwargs)
    def warn(self,  message, **kwargs): self._log.warning(message, **kwargs)
    def error(self, message, **kwargs): self._log.error(message, **kwargs)

client = Brevo(
    api_key="your-api-key",
    logging={"level": "info", "logger": StructlogLogger(), "silent": False},
)
```

#### loguru

```python
from loguru import logger
from brevo import Brevo
from brevo.core.logging import ILogger

class LoguruLogger(ILogger):
    def debug(self, message, **kwargs): logger.bind(**kwargs).debug(message)
    def info(self,  message, **kwargs): logger.bind(**kwargs).info(message)
    def warn(self,  message, **kwargs): logger.bind(**kwargs).warning(message)
    def error(self, message, **kwargs): logger.bind(**kwargs).error(message)

client = Brevo(
    api_key="your-api-key",
    logging={"level": "debug", "logger": LoguruLogger(), "silent": False},
)
```

#### Reusing a pre-built Logger

Build a `Logger` once and reuse it across `Brevo` and `AsyncBrevo` instances:

```python
from brevo import Brevo, AsyncBrevo
from brevo.core.logging import Logger, ConsoleLogger

shared = Logger(level="info", logger=ConsoleLogger(), silent=False)

sync_client  = Brevo(api_key="your-api-key", logging=shared)
async_client = AsyncBrevo(api_key="your-api-key", logging=shared)
```

The default `ConsoleLogger` uses Python's stdlib `logging` module under the logger name `"fern"`. If you only need to filter or reformat output, configuring that logger via `logging.getLogger("fern")` may be enough — you don't always need a custom `ILogger`.

## Custom HTTP client

Override the default `httpx` client for proxies, custom transports, or mTLS:

```python title="custom_http_client.py"
import httpx
from brevo import Brevo

client = Brevo(
    api_key="your-api-key",
    httpx_client=httpx.Client(
        proxy="http://my.test.proxy.example.com",
        transport=httpx.HTTPTransport(local_address="0.0.0.0"),
    ),
)
```

### Common integrations

#### Async with custom client

```python
import httpx
from brevo import AsyncBrevo

client = AsyncBrevo(
    api_key="your-api-key",
    httpx_client=httpx.AsyncClient(
        proxy="http://my.test.proxy.example.com",
    ),
)
```

#### Custom timeout and transport

```python
import httpx
from brevo import Brevo

client = Brevo(
    api_key="your-api-key",
    httpx_client=httpx.Client(
        timeout=httpx.Timeout(120.0, connect=10.0),
        transport=httpx.HTTPTransport(retries=3),
    ),
)
```

#### With event hooks

```python
import httpx
from brevo import Brevo

def log_request(request: httpx.Request) -> None:
    print(f"→ {request.method} {request.url}")

def log_response(response: httpx.Response) -> None:
    print(f"← {response.status_code}")

client = Brevo(
    api_key="your-api-key",
    httpx_client=httpx.Client(
        event_hooks={"request": [log_request], "response": [log_response]},
    ),
)
```

## Available services

The `Brevo` and `AsyncBrevo` clients expose the following service namespaces:

| Property                  | Description                                                 |
| ------------------------- | ----------------------------------------------------------- |
| `transactional_emails`    | Send emails, manage templates, blocked contacts and domains |
| `transactional_sms`       | Send SMS messages and view delivery statistics              |
| `transactional_whats_app` | Send WhatsApp messages and view event reports               |
| `sms_templates`           | Manage SMS templates                                        |
| `contacts`                | Manage contacts, lists, folders, attributes and segments    |
| `email_campaigns`         | Create and manage email marketing campaigns                 |
| `sms_campaigns`           | Create and manage SMS marketing campaigns                   |
| `whats_app_campaigns`     | Create and manage WhatsApp campaigns and templates          |
| `companies`               | Manage CRM companies                                        |
| `deals`                   | Manage CRM deals and pipelines                              |
| `tasks`                   | Manage CRM tasks                                            |
| `notes`                   | Manage CRM notes                                            |
| `files`                   | Upload and manage CRM files                                 |
| `conversations`           | Manage conversation messages and automated messages         |
| `ecommerce`               | Manage products, categories, orders and attribution         |
| `coupons`                 | Manage coupon collections and coupons                       |
| `payments`                | Create and manage payment requests                          |
| `event`                   | Track custom events                                         |
| `webhooks`                | Manage webhooks                                             |
| `senders`                 | Manage senders and IPs                                      |
| `domains`                 | Manage and authenticate domains                             |
| `account`                 | Retrieve account information and activity logs              |
| `inbound_parsing`         | Retrieve inbound email events and attachments               |
| `custom_objects`          | Manage custom object records                                |
| `external_feeds`          | Manage external RSS feeds                                   |
| `master_account`          | Manage sub-accounts and groups (enterprise)                 |
| `user`                    | Manage users and permissions                                |
| `process`                 | Retrieve background process status                          |
| `program`                 | Manage loyalty programs                                     |
| `balance`                 | Manage loyalty balances and transactions                    |
| `reward`                  | Manage loyalty rewards and vouchers                         |
| `tier`                    | Manage loyalty tiers and tier groups                        |

## Migration from v1.x

### Key changes

| Area     | v1.x (`brevo_python`)                    | v4.x (`brevo`)                          |
| -------- | ---------------------------------------- | --------------------------------------- |
| Module   | `import brevo_python`                    | `from brevo import Brevo`               |
| Client   | `AccountApi(ApiClient(config))`          | `Brevo(api_key="...")`                  |
| Config   | `Configuration()` + `api_key['api-key']` | Constructor parameter `api_key`         |
| Errors   | `ApiException`                           | `ApiError` with `.status_code`, `.body` |
| HTTP     | `urllib3`                                | `httpx`                                 |
| Async    | Not available                            | `AsyncBrevo`                            |
| Retries  | Not built-in                             | Automatic with exponential backoff      |
| Timeouts | Manual                                   | 60s default, configurable               |
| Python   | 2.7, 3.4+                                | 3.8+                                    |

### Migration example

```python title="v1_example.py"
# v1.x
import brevo_python
from brevo_python.rest import ApiException

configuration = brevo_python.Configuration()
configuration.api_key['api-key'] = 'YOUR_API_KEY'

api_instance = brevo_python.AccountApi(
    brevo_python.ApiClient(configuration)
)
account = api_instance.get_account()
```

```python title="v4_example.py"
# v4.x
from brevo import Brevo

client = Brevo(api_key="YOUR_API_KEY")

account = client.account.get_account()
```

## Resources

* [GitHub Repository](https://github.com/getbrevo/brevo-python/tree/main)
* [PyPI Package](https://pypi.org/project/brevo-python/)
* [API Reference](https://developers.brevo.com/reference)
* [Support](mailto:support@brevo.com)