Releases: getbrevo/brevo-python
v5.0.0-rc.1
v5.0.0-rc.1
Release candidate for the v5 line. This pre-release is published so adopters can validate the new surface against their integrations before v5.0.0 is tagged stable. It is not picked up by pip install --upgrade automatically — opt in explicitly (see Installing this RC below).
If no blocking issues surface within the soak window, this build will be promoted to v5.0.0 unchanged.
pip install --pre "brevo-python>=5.0.0rc1,<6"Status
- Stability:
rc— API surface is frozen. Only critical regressions discovered during soak will be patched into an rc2. - v4.x: remains supported and continues to receive wire-compatibility fixes.
- Feedback welcome: please open an issue against
getbrevo/brevo-pythonif you hit anything unexpected during migration. Tag withv5-rc.
Why this release
Most of the breaking changes in v5 come from an internal effort at Brevo to make our API endpoints, parameters and models more self-descriptive. The goal is to make the public surface easier to read at a glance — both for developers and for AI agents working against the Brevo API — so that names, shapes and required fields convey intent without needing to cross-reference external docs. Concretely, this means consistent snake_case keyword names, payload wrappers that reflect what the endpoint actually does (e.g. events= instead of request=), filter keys that match the wire format, model fields renamed or removed where the previous names were ambiguous, and shape collapses where a one-field pydantic wrapper added noise without value (Union[str, List[str]], Dict[str, int], etc.).
Every change applies symmetrically to both Brevo (sync) and AsyncBrevo.
We're aware that renaming costs callers a one-time migration, and we've kept v4.x supported so you can adopt v5 on your own timeline.
⚠️ Breaking changes
Companies — get_companies(filters=...) renamed
- The
filterskeyword is renamed tofilters_attributes_nameto match the wire format. - The old keyword is silently dropped, the request still succeeds, and the server returns an unfiltered list. Audit every call site.
Events — event.create_batch_events payload renamed
- Keyword renamed:
request=→events=. - Item types renamed in lockstep:
CreateBatchEventsRequestItem→CreateBatchEventsRequestEventsItem(and 5 sub-types).
Balance — get_contact_balances requires balance_definition_id
balance_definition_idis now a required parameter.
Balance — get_active_balances_api response shape changed
- Response type replaced with
GetLoyaltyBalanceProgramsPidActiveBalanceResponse(different shape).
Balance — begin_transaction.event_time type tightened
str→datetime. Runtime-tolerant underskip_validation, but type-checkers will fail.
CRM — tasks.get_all_task_types returns a list
- Return type changed from
GetCrmTasktypesResponsetoList[GetCrmTasktypesResponseItem]. - Old
.id/.titleaccess on the response now fails (it's a list).
Webhooks — create_webhook(events=...) is now optional / keyword-only
- The
eventsargument moved from required to optional. Positional callers break — switch to keyword arguments.
Webhooks — message_id is now a string
- Type changed from
inttostron history-fetch endpoints.
Response fields removed
GetAccountResponse.date_time_preferencesGetWebhook.channelGetProcessResponseInfo.export
Shape collapses
SendTransacSmsTagis nowUnion[str, List[str]](was a pydantic model).GetExtendedCampaignStats.links_statsis nowDict[str, int](was a pydantic class).ConversationsMessageAttachmentsItem:file_name→name,inline_id→link.
Top-level imports removed (21 names)
- 21 names removed from top-level
from brevo import .... Most notably:CreateBatchEventsRequestItem*(6 names)GetWebhookChannelGetAccountResponseDateTimePreferencesGetExtendedCampaignStatsLinksStatsSendTransacSmsTagFieldUpsertrecordsRequestRecordsItemAssociationsItemZero/One*(8 names)
Added
- New optional fields and filters across
contacts.create_contact,contacts.update_contact,email_campaigns.get_email_campaigns,ecommerce.get_products, and several other endpoints. - Both
Brevo(sync) andAsyncBrevoare affected symmetrically by every change above.
Installing this RC
pip only installs pre-releases when you ask for them explicitly. Either:
pip install --pre "brevo-python>=5.0.0rc1,<6"Or pin the exact RC build:
pip install "brevo-python==5.0.0rc1"For requirements.txt-based projects, add the constraint and run pip with --pre:
brevo-python>=5.0.0rc1,<6
For Poetry / PDM, request the pre-release explicitly:
poetry add "brevo-python@^5.0.0-rc.1" --allow-prereleasesWhen v5.0.0 ships stable, pip install --upgrade brevo-python will swap this RC for the stable release automatically (the --pre flag is no longer needed once stable exists).
Holding on v4.x
If you're not ready to migrate, pin to v4:
pip install "brevo-python>=4,<5"Migration
See the Upgrading from v4.x guide for full migration details.
Documentation
v4.0.10
Bug fixes
get_process / get_processes — duplicate_email_id field type corrected
- Changed from
Optional[int]toOptional[str]to match the actual API response (a URL to a CSV file).
get_process / get_processes — in_process added to status literal
- Both status types now include
"in_process"in theirLiteralunion, so deserialization no longer fails on active processes.
create_event / create_batch_events — contact_properties and event_properties accept booleans
- Value type widened from
Union[str, int]toUnion[str, int, bool].
create_contact / update_contact — attributes accepts integers
- Value type widened from
Union[float, str, bool, List[str]]toUnion[float, int, str, bool, List[str]], so plainintvalues no longer need to be cast tofloat.
v4.0.9
v4.0.9
Bug fix
Fixed ApiError raised on create_contact when contact already exists (update_enabled=True)
When calling create_contact with update_enabled=True on an existing contact, the API correctly returns 204 No Content. The SDK was attempting to parse the empty response body as JSON, causing an unexpected ApiError instead of returning successfully. We are currently looking into additional modules which could result in similar behavior.
This no longer raises ApiError on the second call
client.contacts.create_contact(email=email, attributes={"USER_ID": 456}, update_enabled=True)
Thanks to @pouzet-pass for the detailed report #30
v4.0.8
New features
Events
- Added
get_events()— retrieve a paginated list of custom events, filterable bycontact_id,event_name,object_type,start_date, andend_date.
Note: currently only supports custom events. - Added
create_batch_events()— create multiple events in a single request using the newCreateBatchEventsRequestItemtype.
Ecommerce
create_update_product()and batch product upsert now accept two new optional fields:brandanddescription.
Balance
get_active_balances_api()now accepts an optionalinclude_internalparameter to include balances tied to internal definitions.get_transaction_history()now supports filtering bystatusandtransaction_type(replaces the previoussort_fieldenum).create_balance_definition()andupdate_balance_definition()now accept an optionalmetafield.
Changes
Ecommerce
metaInfosize limit for products clarified: maximum 20,000 characters total (previously documented as 1,000 KB / 20 items).
Account
usersfield on plan verticals is now nullable.
Bug fixes / corrections
InternalServerErrorBodyandInternalServerErrorBodyCodeerror types are now exposed at the top-level package for consistent error handling.
v4.0.7
v4.0.7
Added
- Batch events endpoint —
client.event.create_batch_events()now available to track multiple contact interactions in a single request - Email campaign
exclude_html_contentparameter —get_email_campaign()accepts a new optional flag to omit the HTML body from the response, reducing payload size
Fixed
GetAccountResponsePlanVerticalsItem.usersnullable —usersis now correctly typed asOptional[GetAccountResponsePlanVerticalsItemUsers], preventingTypeErrorwhen the API returnsnullfor certain plan typesGetCampaignStatsnullable fields —apple_mpp_opensandopens_rateare now correctly typed asOptional[int]andOptional[float], preventing errors when the API returnsnullOrderProductsItemall fields —price,product_id,variant_id,quantity, andquantity_floatare all correctly exposed. Previously the type was incompletesend_transac_smsmarked deprecated — Usesend_async_transactional_sms()instead
Internal
- Generator updated to
4.63.3 - Fern CLI updated to
4.22.0
v4.0.6
- Added support for loyalty endpoint to delete membership.
- Buf fixes and improvements
V4.0.5
brevo-python v4.0.3
A complete rewrite of the Brevo Python SDK. The v4 SDK replaces the Swagger Codegen-based brevo_python module with a modern, Fern-generated brevo client built on httpx and pydantic.
This is a major release with breaking changes. See the migration guide below.
Installation
pip install brevo-python==4.0.1What's new
- Single client entry point —
Brevo(api_key="...")replaces per-API class instantiation (AccountApi,ContactsApi, etc.) - Async support — native
AsyncBrevoclient for non-blocking calls - Typed models — Pydantic-based request/response objects with full type annotations
- Automatic retries — exponential backoff on
408,429, and5xx(default: 2 retries) - Configurable timeouts — 60s default, overridable per-client or per-request
- Raw response access — inspect headers and status codes via
.with_raw_response - Custom HTTP client — pass your own
httpx.Clientfor proxies, transports, or mTLS - Modern tooling — Poetry build system, mypy strict mode, ruff linting
Quick start
from brevo import Brevo
client = Brevo(api_key="YOUR_API_KEY")
account = client.account.get_your_account_information_plan_and_credits_details()
print(account)Async usage
import asyncio
from brevo import AsyncBrevo
client = AsyncBrevo(api_key="YOUR_API_KEY")
async def main() -> None:
account = await client.account.get_your_account_information_plan_and_credits_details()
print(account)
asyncio.run(main())Requirements
- Python 3.8+ (Python 2.7 and 3.4-3.7 are no longer supported)
httpx>= 0.21.2pydantic>= 1.9.2typing_extensions>= 4.0.0
Breaking changes
Python version support
| v1.x | v4.x |
|---|---|
| Python 2.7, 3.4+ | Python 3.8+ |
Import path
| v1.x | v4.x |
|---|---|
import brevo_python |
from brevo import Brevo |
Client initialization
# v1.x
import brevo_python
configuration = brevo_python.Configuration()
configuration.api_key['api-key'] = 'YOUR_API_KEY'
api_instance = brevo_python.AccountApi(brevo_python.ApiClient(configuration))
api_response = api_instance.get_account()
# v4.x
from brevo import Brevo
client = Brevo(api_key="YOUR_API_KEY")
account = client.account.get_your_account_information_plan_and_credits_details()Error handling
# v1.x
from brevo_python.rest import ApiException
try:
api_response = api_instance.get_account()
except ApiException as e:
print("Exception: %s\n" % e)
# v4.x
from brevo.core.api_error import ApiError
try:
account = client.account.get_your_account_information_plan_and_credits_details()
except ApiError as e:
print(e.status_code)
print(e.body)HTTP library
| v1.x | v4.x |
|---|---|
urllib3 (via Swagger Codegen) |
httpx |
| No async support | AsyncBrevo with httpx.AsyncClient |
Build system
| v1.x | v4.x |
|---|---|
setup.py |
pyproject.toml (Poetry) |
requirements.txt with urllib3, certifi, six |
httpx, pydantic, typing_extensions |
Summary of all breaking 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+ |
| Build | setup.py |
pyproject.toml (Poetry) |
| Generator | Swagger Codegen | Fern |
Migrating from v1.x
Step 1 — Update the package
pip install --upgrade brevo-python==4.0.0Step 2 — Update imports
Replace all brevo_python imports:
# Before
import brevo_python
from brevo_python.rest import ApiException
# After
from brevo import Brevo
from brevo.core.api_error import ApiErrorStep 3 — Replace client initialization
Remove Configuration and ApiClient boilerplate:
# Before
configuration = brevo_python.Configuration()
configuration.api_key['api-key'] = 'YOUR_API_KEY'
api_instance = brevo_python.ContactsApi(brevo_python.ApiClient(configuration))
# After
client = Brevo(api_key="YOUR_API_KEY")Step 4 — Update API calls
Methods are now accessed through namespaced properties on a single client:
# Before
contacts_api = brevo_python.ContactsApi(brevo_python.ApiClient(configuration))
result = contacts_api.get_contacts()
email_api = brevo_python.TransactionalEmailsApi(brevo_python.ApiClient(configuration))
email_api.send_transac_email(message)
# After
result = client.contacts.get_all_the_contacts()
client.transactional_emails.send_a_transactional_email(...)Step 5 — Update error handling
# Before
from brevo_python.rest import ApiException
try:
api_response = api_instance.get_account()
except ApiException as e:
print("Exception: %s\n" % e)
# After
from brevo.core.api_error import ApiError
try:
account = client.account.get_your_account_information_plan_and_credits_details()
except ApiError as e:
print(e.status_code)
print(e.body)Deprecation notice
The v1.x SDK on the main branch will continue to receive critical security patches but no new features or API coverage updates. All new development targets v4.x on the v4 branch.
Links
v1.2.0
What's Changed
- Sync with all the latest updates.
v1.1.2
v1.1.1
What's Changed
New API's added:
- Coupons.
- Deals.
- Ecommerce.
- Events.
- Master account.
- Payments.
- User.