PHP SDK

Learn how to integrate the Brevo API into your PHP applications.

Overview

The Brevo PHP SDK (getbrevo/brevo-php) is a type-safe client library for the Brevo API. It provides:

  • A unified Brevo client with namespaced service clients
  • Strongly typed request and response objects with full PHPDoc annotations
  • PSR-18 HTTP client compatibility (Guzzle, Symfony HttpClient, etc.)
  • Automatic retries with exponential backoff
  • Structured error handling via BrevoApiException

Packagist

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

Requirements

  • PHP 8.1+
  • ext-json
  • A PSR-18 HTTP client (e.g., Guzzle, Symfony HttpClient)

Installation

Install the SDK using Composer:

$composer require getbrevo/brevo-php

Guzzle is recommended as the HTTP client:

$composer require getbrevo/brevo-php guzzlehttp/guzzle

If no HTTP client is provided, the SDK uses php-http/discovery to auto-detect an installed PSR-18 client.

Quick start

Initialize the client and send your first email:

quick_start.php
1<?php
2
3require_once __DIR__ . '/vendor/autoload.php';
4
5use Brevo\Brevo;
6use Brevo\TransactionalEmails\Requests\SendTransacEmailRequest;
7use Brevo\TransactionalEmails\Types\SendTransacEmailRequestSender;
8use Brevo\TransactionalEmails\Types\SendTransacEmailRequestToItem;
9
10$client = new Brevo(apiKey: 'your-api-key');
11
12$result = $client->transactionalEmails->sendTransacEmail(
13 new SendTransacEmailRequest([
14 'subject' => 'Hello from Brevo!',
15 'htmlContent' => '<html><body><p>Hello,</p><p>This is my first transactional email.</p></body></html>',
16 'sender' => new SendTransacEmailRequestSender([
17 'name' => 'Alex from Brevo',
18 'email' => 'hello@brevo.com',
19 ]),
20 'to' => [
21 new SendTransacEmailRequestToItem([
22 'email' => 'johndoe@example.com',
23 'name' => 'John Doe',
24 ]),
25 ],
26 ])
27);
28
29echo 'Email sent. Message ID: ' . $result->messageId . PHP_EOL;

Configuration

Pass an options array as the second argument to configure the client:

configuration.php
1$client = new Brevo('your-api-key', [
2 'timeout' => 30, // seconds
3 'maxRetries' => 3,
4]);

Constructor parameters

ParameterTypeDefaultDescription
apiKeystringRequiredYour Brevo API key
timeoutfloatnullRequest timeout in seconds. Defaults to the underlying HTTP client’s default.
baseUrlstringnullOverride the default API base URL
maxRetriesint2Maximum retry attempts on retryable errors
clientClientInterfacenullCustom PSR-18 HTTP client
headersarray<string, string>nullAdditional default headers sent with every request

Error handling

BrevoApiException is thrown for any non-2xx HTTP response. Catch it to inspect the status code and response body:

error_handling.php
1use Brevo\Exceptions\BrevoApiException;
2use Brevo\Exceptions\BrevoException;
3
4try {
5 $client->transactionalEmails->sendTransacEmail($request);
6} catch (BrevoApiException $e) {
7 $statusCode = $e->getCode();
8 $body = $e->getBody();
9
10 if ($statusCode === 401) {
11 echo 'Invalid API key';
12 } elseif ($statusCode === 429) {
13 echo 'Rate limited. Check Retry-After header.';
14 } else {
15 echo "API error {$statusCode}: " . $e->getMessage();
16 }
17} catch (BrevoException $e) {
18 echo 'SDK error: ' . $e->getMessage();
19}

Exception classes

ClassDescription
BrevoApiExceptionThrown for non-2xx HTTP responses
BrevoExceptionBase class for all SDK-level errors

BrevoApiException exposes:

  • getCode() — HTTP status code
  • getMessage() — Error message
  • getBody() — Parsed response body

Status codes

CodeMeaning
400Bad Request
401Unauthorized
403Forbidden
404Not Found
422Unprocessable Entity
429Too Many Requests
5xxServer Error

Retries

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

retries.php
1// Client-level
2$client = new Brevo('your-api-key', [
3 'maxRetries' => 3,
4]);
5
6// Request-level (overrides client setting)
7$client->transactionalEmails->sendTransacEmail($request, [
8 'maxRetries' => 0, // Disable retries for this request
9]);

Retry behavior

  • Retryable status codes: 408, 429, 5xx
  • Backoff schedule: ~1s, ~2s, ~4s (exponential, base 1000ms) with ±10% symmetric jitter
  • Maximum delay: 60s per retry interval
  • Rate limit headers: Respects Retry-After and X-RateLimit-Reset response headers

Timeouts

No default timeout is configured. Unless you set one, the underlying HTTP client’s default applies (Guzzle defaults to no timeout). Set an explicit timeout at the client or request level:

timeouts.php
1// Client-level
2$client = new Brevo('your-api-key', [
3 'timeout' => 30,
4]);
5
6// Request-level (overrides client setting)
7$client->transactionalEmails->sendTransacEmail($request, [
8 'timeout' => 10,
9]);

Timeout forwarding is supported for Guzzle and Symfony HttpClient. For other PSR-18 clients, the timeout value is ignored and a PHP warning is triggered.

Use caseTimeout
Standard API calls30–60s
Quick operations10–15s
Bulk operations120–300s
Real-time / low-latency5–10s

Request options

The timeout, maxRetries, and headers options can be overridden per request by passing an options array as the last argument:

request_options.php
1$client->transactionalEmails->sendTransacEmail($request, [
2 'timeout' => 10,
3 'maxRetries' => 1,
4 'headers' => [
5 'X-Custom-Header' => 'custom-value',
6 ],
7]);

Query parameters

Pass query parameters via the typed request object:

query_params.php
1use Brevo\Contacts\Requests\GetContactsRequest;
2
3$client->contacts->getContacts(new GetContactsRequest([
4 'limit' => 50,
5 'offset' => 0,
6]));

Binary responses

Some endpoints (e.g., attachment downloads) return binary content directly:

binary_response.php
1$content = $client->inboundParsing->getInboundEmailAttachment($downloadToken);
2
3file_put_contents('path/to/file', $content);

Type safety

All request and response objects are strongly typed. Use the generated request classes for IDE autocomplete and static analysis:

type_safety.php
1use Brevo\TransactionalEmails\Requests\SendTransacEmailRequest;
2use Brevo\TransactionalEmails\Types\SendTransacEmailRequestSender;
3use Brevo\TransactionalEmails\Types\SendTransacEmailRequestToItem;
4
5$request = new SendTransacEmailRequest([
6 'subject' => 'First email',
7 'textContent' => 'Hello world!',
8 'sender' => new SendTransacEmailRequestSender([
9 'name' => 'Bob Wilson',
10 'email' => 'bob.wilson@brevo.com',
11 ]),
12 'to' => [
13 new SendTransacEmailRequestToItem([
14 'email' => 'sarah.davis@example.com',
15 'name' => 'Sarah Davis',
16 ]),
17 ],
18]);

Custom HTTP client

Pass any PSR-18-compatible client via the client option:

custom_http_client.php
1use Brevo\Brevo;
2use GuzzleHttp\Client;
3
4$client = new Brevo('your-api-key', [
5 'client' => new Client(['timeout' => 5.0]),
6]);

Common integrations

1use GuzzleHttp\Client;
2
3$client = new Brevo('your-api-key', [
4 'client' => new Client(['timeout' => 5.0]),
5]);
1use Symfony\Component\HttpClient\Psr18Client;
2use Symfony\Component\HttpClient\HttpClient;
3
4$client = new Brevo('your-api-key', [
5 'client' => new Psr18Client(
6 HttpClient::create(['timeout' => 5.0])
7 ),
8]);

Register the client as a singleton in a service provider:

1// AppServiceProvider.php
2use Brevo\Brevo;
3
4public function register(): void
5{
6 $this->app->singleton(Brevo::class, function () {
7 return new Brevo(apiKey: config('services.brevo.api_key'));
8 });
9}

Logging

The SDK doesn’t ship a built-in logger. Instead, it routes every request through the PSR-18 HTTP client you pass via the client option, which means you can plug in any PSR-3 logger (Monolog, Symfony Logger, Laravel’s Log facade, etc.) by wrapping that client with logging middleware.

This keeps the SDK lean and lets you reuse the logger your application already configures.

logging_guzzle.php
1use Brevo\Brevo;
2use GuzzleHttp\Client;
3use GuzzleHttp\HandlerStack;
4use GuzzleHttp\Middleware;
5use GuzzleHttp\MessageFormatter;
6use Monolog\Logger;
7use Monolog\Handler\StreamHandler;
8
9$logger = new Logger('brevo');
10$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));
11
12$stack = HandlerStack::create();
13$stack->push(Middleware::log(
14 $logger,
15 new MessageFormatter('{method} {uri} → {code} ({res_header_Content-Length} bytes)')
16));
17
18$client = new Brevo('your-api-key', [
19 'client' => new Client(['handler' => $stack, 'timeout' => 5.0]),
20]);

What gets logged

Logging happens at the HTTP layer, so each Brevo SDK call produces a log line per HTTP request — including retries, which the SDK performs internally. Use the message formatter to control verbosity (method/URI only, full headers, request and response bodies, timing, etc.).

Middleware::log placeholders worth knowing:

PlaceholderDescription
{method}HTTP method (GET, POST, …)
{uri}Full request URI
{code}Response status code
{req_headers} / {res_headers}Full headers (be careful — includes api-key)
{req_body} / {res_body}Request and response bodies
{error}Error message when a network error occurs

The api-key header is sent on every request. If you log request headers, redact it before writing to disk or shipping to an external sink. The simplest approach is a custom middleware that strips the header before the log middleware runs.

Common integrations

Symfony’s Psr18Client accepts a PSR-3 logger via the underlying HttpClient. It logs requests, responses, and retries automatically — no middleware wiring required.

1use Brevo\Brevo;
2use Symfony\Component\HttpClient\HttpClient;
3use Symfony\Component\HttpClient\Psr18Client;
4use Symfony\Component\HttpClient\LoggerAwareInterface;
5
6$http = HttpClient::create(['timeout' => 5.0]);
7$http->setLogger($yourPsr3Logger); // Monolog, Symfony Logger, anything PSR-3
8
9$client = new Brevo('your-api-key', [
10 'client' => new Psr18Client($http),
11]);

Reuse Laravel’s configured logger by resolving it from the service container when you build the Brevo client:

1// AppServiceProvider.php
2use Brevo\Brevo;
3use GuzzleHttp\Client;
4use GuzzleHttp\HandlerStack;
5use GuzzleHttp\Middleware;
6use GuzzleHttp\MessageFormatter;
7use Psr\Log\LoggerInterface;
8
9public function register(): void
10{
11 $this->app->singleton(Brevo::class, function ($app) {
12 $stack = HandlerStack::create();
13 $stack->push(Middleware::log(
14 $app->make(LoggerInterface::class),
15 new MessageFormatter('{method} {uri} → {code}')
16 ));
17
18 return new Brevo(
19 apiKey: config('services.brevo.api_key'),
20 options: ['client' => new Client(['handler' => $stack])],
21 );
22 });
23}

Any object implementing Psr\Log\LoggerInterface works with Guzzle’s Middleware::log. This lets you drop the SDK into projects that use Monolog, the Symfony Logger, Laminas Log, or a hand-rolled PSR-3 implementation without changing the SDK call sites.

PHP-specific considerations

API key from environment

Avoid hardcoding API keys. Use environment variables:

env_api_key.php
1$client = new Brevo(apiKey: $_ENV['BREVO_API_KEY'] ?? getenv('BREVO_API_KEY'));

In Laravel, use config('services.brevo.api_key'). In Symfony, use $_ENV['BREVO_API_KEY'] or service parameters.

Memory limits

For large operations (bulk exports, large attachments), adjust PHP’s memory_limit:

1ini_set('memory_limit', '256M');

Available services

The unified Brevo client exposes the following service namespaces:

PropertyDescription
transactionalEmailsSend emails, manage templates, blocked contacts and domains
transactionalSmsSend SMS messages and view delivery statistics
transactionalWhatsAppSend WhatsApp messages and view event reports
smsTemplatesManage SMS templates
contactsManage contacts, lists, folders, attributes and segments
emailCampaignsCreate and manage email marketing campaigns
smsCampaignsCreate and manage SMS marketing campaigns
whatsAppCampaignsCreate and manage WhatsApp campaigns and templates
companiesManage CRM companies
dealsManage CRM deals and pipelines
tasksManage CRM tasks
notesManage CRM notes
filesUpload and manage CRM files
conversationsManage conversation messages and automated messages
ecommerceManage products, categories, orders and attribution
couponsManage coupon collections and coupons
paymentsCreate and manage payment requests
eventTrack custom events
webhooksManage webhooks
sendersManage senders and IPs
domainsManage and authenticate domains
accountRetrieve account information and activity logs
inboundParsingRetrieve inbound email events and attachments
customObjectsManage custom object records
externalFeedsManage external RSS feeds
masterAccountManage sub-accounts and groups (enterprise)
userManage users and permissions
processRetrieve background process status
programManage loyalty programs
balanceManage loyalty balances and transactions
rewardManage loyalty rewards and vouchers
tierManage loyalty tiers and tier groups

Migration from the legacy SDK

Key changes

AreaLegacy SDKv4.x
Importuse Brevo\Client\Api\*use Brevo\Brevo
Client initnew TransactionalEmailsApi($httpClient, $config)new Brevo(apiKey: 'api-key')
ConfigConfiguration::getDefaultConfiguration()Constructor options array
RequestsSetter methods (->setSubject(...))Typed request objects
ErrorsGuzzle exceptionsBrevoApiException with getCode(), getBody()
HTTP clientHard Guzzle dependencyPSR-18 (any compatible client)
RetriesNot built-inAutomatic with exponential backoff
PHP7.x+8.1+

Migration example

1// Legacy SDK
2use Brevo\Client\Configuration;
3use Brevo\Client\Api\TransactionalEmailsApi;
4use Brevo\Client\Model\SendSmtpEmail;
5
6$config = Configuration::getDefaultConfiguration()
7 ->setApiKey('api-key', 'xkeysib-xxx');
8
9$api = new TransactionalEmailsApi(new \GuzzleHttp\Client(), $config);
10
11$message = new SendSmtpEmail();
12$message->setSubject('First email');
13$message->setTextContent('Hello world!');
14$message->setSender(['name' => 'Bob Wilson', 'email' => 'bob.wilson@brevo.com']);
15$message->setTo([['email' => 'sarah.davis@example.com', 'name' => 'Sarah Davis']]);
16
17$api->sendTransacEmail($message);

Resources