Brevo API: практическо ръководство за разработчици
Ръководство за Brevo API за разработчици: удостоверяване, базов URL, контакти, транзакционни имейли, кампании, CRM обекти, webhooks, ограничения на заявките и реалните лимити.
Brevo предоставя един REST API, който обхваща транзакционните съобщения, маркетинговите кампании, данните за контактите и CRM записите. Първата заявка да върне 201 отнема около две минути. Производствена интеграция, която не губи тихо данни, отнема значително повече, защото няколко от най-важните ограничения или не са документирани, или противоречат на това, което API съобщава за себе си.
Това ръководство покрива и двете половини: крайните адреси, SDK и удостоверяването, които са Ви нужни в първия ден, и платформените лимити, около които трябва да проектирате, преди да пуснете в production.
Какво покрива Brevo API
Всичко живее под един хост и един път с версия. Документацията за разработчици групира повърхността в четири продуктови области:
- Съобщения: транзакционни имейли, SMS и WhatsApp, включително групови изпращания, планиране и активност на съобщенията.
- Маркетингова платформа: контакти, списъци, сегменти и имейл кампании.
- Електронна търговия: продукти, поръчки и проследяване на събития за клиентите.
- Разговори: чат приспособлението и програмното управление на разговори.
Тези области споделят един акаунт, една база от контакти и един API ключ. Това е удобно и понякога опасно: скрипт, писан срещу представата Ви за тестови данни, говори със същите контакти, до които изпращат кампаниите Ви.
Транзакционни спрямо маркетингови
Двете семейства се държат достатъчно различно, за да е тяхното смесване най-честата проектантска грешка.
| Транзакционни | Маркетингови | |
|---|---|---|
| Основен краен адрес | POST /v3/smtp/email | POST /v3/emailCampaigns |
| Адресиране | Изрични получатели в заявката | listIds или segmentIds |
| Тригер | Вашето приложение, в реално време | Планирано или изпратено при поискване |
| Типична форма на обема | Непрекъснат, по едно съобщение | На вълни, едно голямо изпращане |
| Позиция спрямо лимитите | Много висока, 1000 заявки в секунда при стандартните планове | Ниска, крайните адреси за кампании попадат под общия таван |
Ако още преценявате дали Brevo изобщо е правилната платформа, прегледът на платформата покрива тази територия.
Удостоверяване и управление на ключове
Brevo използва обикновен API ключ в собствен хедър. Хедърът се казва api-key, а не Authorization, и няма префикс Bearer. Това обърква почти всеки, който първо е работил с друг API за съобщения.
curl https://api.brevo.com/v3/account \ -H "api-key: $BREVO_API_KEY"Ключовете се генерират в приложението на Brevo, в настройките на акаунта, в раздела SMTP and API, на таба API keys. Дайте на всеки ключ описателно име, обвързано със системата, която го използва. Стойността на ключа се показва точно веднъж при генерирането, така че ако я загубите, генерирате нов ключ, вместо да възстановявате стария.
Няколко практични правила:
- Издавайте отделен ключ за всяка среда и всяка услуга. Отнемането на компрометиран ключ никога не бива да сваля три несвързани системи.
- Стандартните API ключове важат за целия акаунт. Третирайте всеки ключ като пълен достъп до контактите, изпращането и CRM данните.
- Brevo поддържа и OAuth 2.0 за приложения, които действат от името на други Brevo акаунти, описан заедно с потока с ключове в схемите за удостоверяване.
- MCP сървърът, използван от AI асистенти, приема отделен токен и наистина използва bearer хедър. Този токен се генерира в същия екран с API ключове, но не е взаимозаменяем с REST ключ.
Базов URL, версии и Вашият първи запис
Базовият URL е https://api.brevo.com/v3/. Версията е в пътя, а не в хедър, и v3 е текущото поколение. Всеки път в това ръководство е относителен спрямо тази база.
Първият запис е по-информативен от първото четене, защото натоварва частите от акаунта, които обикновено са конфигурирани грешно, най-вече потвърдените изпращачи:
curl -X POST https://api.brevo.com/v3/smtp/email \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender": { "name": "Ops", "email": "[email protected]" }, "to": [{ "email": "[email protected]", "name": "Dev" }], "subject": "First transactional send", "htmlContent": "<html><body><p>It works.</p></body></html>", "tags": ["smoke-test"] }'Успешното изпращане връща 201 с messageId. Планираното изпращане връща 202.
Крайните адреси, които наистина ще използвате
Контакти
POST /v3/contacts създава контакт. Тялото приема email, карта attributes за персонализирани полета, listIds, ext_id за Вашия собствен външен ключ и двата флага, които имат най-голямо значение на практика: updateEnabled, който превръща заявката в upsert, и getId, който кара отговора да върне идентификатора на контакта.
Четенето минава през GET /v3/contacts, който страницира с limit (по подразбиране 50, максимум 1000) и offset и поддържа modifiedSince и createdSince в UTC. Инкременталните синхронизации трябва да стъпват на modifiedSince, вместо да обхождат целия списък. Обърнете внимание, че параметърът filter поддържа само оператор за равенство, така че всичко по-изразително е работа за сегмент.
За групово зареждане POST /v3/contacts/import приема fileUrl, fileBody или jsonBody, насочва се към listIds и работи асинхронно, връщайки processId. Brevo документира максимум 10 MB за тялото и препоръчва да останете близо до 8 MB, защото парсването раздува полезния товар. Подайте notifyUrl, за да научите резултата, вместо да го чакате чрез повтарящи се проверки.
Транзакционни имейли
POST /v3/smtp/email е работният кон. Освен sender, to, subject и htmlContent, полетата, които си струва да познавате, са:
templateIdзаедно сparams, което заменя вградено съдържание с шаблон на Brevo и неговите заместващи променливи. Параметрите на отделна версия са ограничени до 100 KB, а натрупаните параметри до 1000 KB.messageVersions, което изпраща персонализирани варианти с една заявка, до 99 получатели на версия.tags, което винаги трябва да задавате. Етикетите се връщат в webhook събитията и са единственият евтин начин да свържете събитие за доставка с кода, който го е породил.scheduledAtзаедно сbatchId, за бъдещи изпращания, които може да искате да отмените наведнъж.headers, изписани с главни букви в началото на всяка дума, за персонализирани SMTP хедъри.
Една заявка приема най-много 2000 получатели. За разликата между този краен адрес и изпращането на кампании ръководството за транзакционни имейли дава гледната точка на стратегията за съобщения.
Имейл кампании
POST /v3/emailCampaigns изисква name и sender, плюс точно един източник на съдържание: htmlContent (минимум 10 знака, под 1 MB), htmlUrl или templateId. Аудиторията отива в recipients като listIds или segmentIds, а scheduledAt използва UTC формата YYYY-MM-DDTHH:mm:ss.SSSZ. Придружаващите маршрути покриват незабавно изпращане, изпращане на тест, промяна на статуса и извличане на отчета за кампанията.
Компании, сделки и обекти
CRM частта на Brevo има два припокриващи се пътя за запис и правилният избор има значение.
CRM маршрутите са POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} и еквивалентният набор за сделки. Те са синхронни. Един PATCH връща 204, щом промяната бъде приложена.
API за обекти е груповият път: POST /v3/objects/{object_type}/batch/upsert приема до 1000 записа и 1 MB на заявка, до 500 атрибута на запис и до 10 асоциирани записа на тип обект за запис. Връща 202 с processId, което означава прието, а не приложено.
curl -X POST https://api.brevo.com/v3/objects/company/batch/upsert \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "identifiers": { "id": 12345 }, "attributes": { "domain": "acme.example", "industry": "retail" } } ] }'Ръководството за Brevo CRM покрива модела на обектите от гледна точка на оператора.
Официални SDK
Brevo поддържа клиенти в GitHub организацията getbrevo:
| Език | Хранилище |
|---|---|
| Node.js | github.com/getbrevo/brevo-node |
| Python | github.com/getbrevo/brevo-python |
| PHP | github.com/getbrevo/brevo-php |
| Java | github.com/getbrevo/brevo-java |
| C# | github.com/getbrevo/brevo-csharp |
| Go | github.com/getbrevo/brevo-go |
| Ruby | github.com/getbrevo/brevo-ruby |
Node клиентът се инсталира като @getbrevo/brevo:
npm install @getbrevo/brevoimport { BrevoClient } from "@getbrevo/brevo";
const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY });
const result = await brevo.transactionalEmails.sendTransacEmail({ subject: "Order confirmed", htmlContent: "<html><body><p>Thanks for your order.</p></body></html>", tags: ["order-confirmation"],});
console.log("Message ID:", result.messageId);Python клиентът се инсталира с pip install brevo-python. Ако предпочитате да не носите SDK зависимост заради два крайни адреса, чистата HTTP повърхност е достатъчно малка, за да я извикате директно, което Ви предпазва и от смяната на версии на SDK:
import osimport requests
BASE = "https://api.brevo.com/v3"HEADERS = { "api-key": os.environ["BREVO_API_KEY"], "Content-Type": "application/json",}
def upsert_contact(email, attributes, list_ids): response = requests.post( f"{BASE}/contacts", headers=HEADERS, json={ "email": email, "attributes": attributes, "listIds": list_ids, "updateEnabled": True, }, timeout=30, ) response.raise_for_status() return responseИма и MCP сървър на адрес https://mcp.brevo.com/v1/brevo/mcp за AI асистенти, удостоверяван с bearer токен, генериран в същия екран с настройки. Полезен е за проучване и въпроси за акаунта, не за производствени пътища на данните.
Webhooks
Webhooks са начинът, по който научавате какво се е случило след изпращане. POST /v3/webhooks създава такъв, с url, events, type и по избор channel (email или sms), batched, персонализирани headers и обект auth.
Има три вида webhooks с различен речник от събития:
- Транзакционни:
sent,request,delivered,hardBounce,softBounce,blocked,spam,invalid,deferred,click,opened,uniqueOpened,unsubscribed. - Маркетингови:
spam,opened,click,hardBounce,softBounce,unsubscribed,listAddition,delivered,contactUpdated,contactDeleted. - Входящи:
inboundEmailProcessedиreply, които допълнително изискватdomain.
curl -X POST https://api.brevo.com/v3/webhooks \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://yourapp.example/hooks/brevo", "type": "transactional", "events": ["delivered", "hardBounce", "spam", "unsubscribed"], "description": "Deliverability signals" }'Три неща трябва да направите правилно. Първо, един акаунт може да съдържа най-много 40 webhooks от всички видове, така че насочвайте по събитие вътре в обработващия код, вместо да регистрирате отделен краен адрес за всяко събитие. Второ, използвайте флага batched, когато очаквате обем, тъй като една заявка с много събития се обработва далеч по-евтино от много заявки. Трето, защитете приемника: Brevo публикува диапазоните на изпращащите си IP адреси и ограничаването на Вашия краен адрес до тези диапазони е документираният подход. Добавете и собствена споделена тайна през полето headers като втори слой.
Обработващият код трябва да е идемпотентен. Използвайте идентификатора на съобщението плюс типа на събитието плюс времевия печат като ключ за премахване на дубликати.
Ограничения на заявките и обработка на грешки
Ограниченията на Brevo са за всеки краен адрес и за всяко ниво на плана, а разликата между крайните адреси е огромна.
| Краен адрес | Стандартен | Professional и Enterprise |
|---|---|---|
POST /v3/smtp/email | 1000 RPS | 2000 RPS |
POST /v3/transactionalSMS/send | 150 RPS | 200 RPS |
/v3/contacts/... | 10 RPS, 36 000 RPH | 20 RPS, 72 000 RPH |
POST /v3/events | 10 RPS, 36 000 RPH | по-високо при Enterprise |
GET /v3/smtp/emails | 2 RPS, 7200 RPH | 3 RPS, 10 800 RPH |
| Всичко останало | 100 RPH | 200 RPH |
Последният ред е този, който боли. Изпращането е практически неограничено, докато управлението на кампании, четенето от CRM и повечето административни заявки делят бюджет от 100 заявки на час при стандартните планове. Наивно попълване назад, което чете запис за компания преди всеки запис, ще изчерпи един час квота за под две минути.
Всеки отговор носи x-sib-ratelimit-limit, x-sib-ratelimit-remaining и x-sib-ratelimit-reset. Четете ги при успех, не само при неуспех. Надвишаването на лимит връща 429, а правилната реакция е да изчакате интервала от хедъра за нулиране и след това да приложите експоненциално отстъпление със случаен разсейващ елемент.
async function callBrevo(path, init, attempt = 0) { const response = await fetch(`https://api.brevo.com/v3${path}`, { ...init, headers: { "api-key": process.env.BREVO_API_KEY, "Content-Type": "application/json", ...init.headers }, });
if (response.status === 429 && attempt < 5) { const reset = Number(response.headers.get("x-sib-ratelimit-reset") || 1); const backoff = Math.pow(2, attempt) * 250 + Math.random() * 250; await new Promise((r) => setTimeout(r, reset * 1000 + backoff)); return callBrevo(path, init, attempt + 1); }
return response;}Повтаряйте при 429 и 5xx. Никога не повтаряйте сляпо 400 или 409, защото и двете обикновено означават, че заявката е грешна, а не подранила, а 409 в частност изисква различно действие, а не повторение.
Тестване без изпращане на поща
Добавете хедъра X-Sib-Sandbox със стойност drop към транзакционно изпращане. Brevo валидира заявката, връща 201 с messageId, не доставя нищо и не записва имейл лог.
curl -X POST https://api.brevo.com/v3/smtp/email \ -H "api-key: $BREVO_API_KEY" \ -H "X-Sib-Sandbox: drop" \ -H "Content-Type: application/json" \ -d '{ "sender": { "email": "[email protected]" }, "to": [{ "email": "[email protected]" }], "subject": "Sandbox", "htmlContent": "<p>hi</p>" }'Разберете какво доказва и какво не доказва това. Пясъчният режим валидира само формата на заявката. Той не казва нищо за удостоверяването на изпращача, за визуализирането на шаблоните или за доставимостта. Пазете отделен Brevo акаунт за интеграционно тестване на всичко, което докосва контакти или CRM данни, защото пясъчният режим покрива изпращането, а не останалата част от API.
Лимити, които определят дизайна на интеграцията Ви
Това са ограниченията, които се появяват едва когато интеграцията работи срещу реален акаунт при обем. Няколко от тях противоречат на това, което API казва за себе си. Никое от тях не подлежи на договаряне, така че единственият разумен отговор е да проектирате около тях.
Компаниите изискват домейн и на един домейн отговаря само една компания
GET /v3/crm/attributes/companies отчита всеки атрибут като незадължителен, а справката за създаване на компания посочва само name като задължително. На практика POST /v3/companies без непразен атрибут domain връща 400 със съобщение за липсващи задължителни атрибути по подразбиране. Празният низ се проваля точно както и пропускането му.
По-лошото е, че уникалността на домейна се налага. Втора компания на домейн, който вече се използва, връща 409. За B2B търговията това е структурно: дъщерни дружества, които споделят един имейл домейн на купувача, не могат да съществуват като отделни компании в Brevo. Синхронизирането на контакт също е достатъчно, за да се появи компания на домейна от имейла му, така че създаването може да се сблъска с компания, която никой не е създал изрично. Правилният обработващ код приема съществуващата компания при 409, вместо да се проваля или да опитва отново.
Недекларираните атрибути се отхвърлят тихо
Това е най-опасното поведение в платформата и Brevo го документира съвсем ясно: ако атрибут се появи в заявка, но не е бил дефиниран предварително в схемата на обекта, не се случва нищо. Няма грешка, няма създаден атрибут, няма предупреждение.
Следователно отговор 2xx не е доказателство, че данните Ви са пристигнали. Прочетете схемата, преди да пишете, отхвърляйте всичко недекларирано във Вашия собствен клиент и отказвайте да пуснете синхронизация, чиито атрибути не съществуват, вместо да записвате половин запис в продължение на месец, преди някой да забележи.
Филтрите по атрибути се приемат и се пренебрегват
GET /v3/companies?filters[attributes.domain]=... връща 200 и пренебрегва филтъра. Два напълно различни филтъра връщат едни и същи записи. Няма работещ начин да намерите компания по атрибут през този маршрут.
В съчетание с факта, че нефилтрираният списък прекъсва с 504 при големи акаунти независимо от размера на страницата, съществуваща компания може да е наистина ненамираема през документирания път. Заобикалянето е да сканирате GET /v3/objects/company/records със sort=desc, което е бързо, страницирано и връща атрибути, ограничено до разумен брой страници. Компания, която току-що е предизвикала 409, почти винаги е създадена секунди по-рано, така че сканирането от най-новото назад я намира бързо.
Един милион записа на тип обект и никакво групово изтриване
POST /v3/objects/{type}/batch/upsert връща 400, щом типът обект достигне един милион записа. Това блокира и обновяванията, не само създаването: адресирането на съществуващ запис по собствения му числов идентификатор се проваля по същия начин. Целият път за запис към обекти се затваря наведнъж.
Връщането под тавана е бавно, защото POST /v3/objects/{type}/batch/delete връща 403 за стандартните типове обекти на Brevo, например company. Единственият маршрут е DELETE /v3/companies/{id}, по един запис на заявка при около 156 ms. Изчистването на 124 000 записа по този начин отне часове с 20 паралелни работника. Следете броя на записите по график, вместо да откривате тавана чрез провалена синхронизация, и насочвайте обновяванията с висок обем през PATCH /v3/companies/{id}, който няма такъв лимит.
ext_id е идентификатор на Brevo, а не Ваш
При записите за обекти identifiers.ext_id съдържа собствения CRM идентификатор на компанията в Brevo, низ в стил Mongo. Той не е свободен външен ключ. Ако направите upsert по ext_id, зададен на идентификатора от Вашата платформа, ще създадете дубликати вместо съвпадения. Вашият външен идентификатор принадлежи на отделен деклариран атрибут.
Upsert към обекти е асинхронен, записите в CRM не са
batch/upsert връща 202 и processId, а прилага по-късно. Несъществуващ идентификатор се проваля асинхронно и въпреки това връща 202 на извикващия. PATCH /v3/companies/{id} връща 204 и се прилага синхронно. Ако синхронизацията Ви докладва успех, само синхронният път заслужава тази дума без последващо четене.
Кратък списък за проверка на интеграцията
- Отделни API ключове за всяка услуга и всяка среда, сменяни при промени в екипа.
- Всички записи минават през един клиент, който чете хедърите за ограниченията и отстъпва при 429.
- Схемата на атрибутите се проверява при стартиране, а синхронизацията отказва да работи, ако атрибутите ѝ липсват.
- 409 при създаване на компания означава приемане, а не повторение.
- Груповите пътища използват API за обекти заради пропускателната способност и CRM маршрутите за всичко, което трябва да бъде потвърдено.
- Webhooks са идемпотентни, групирани, ограничени по IP и носят хедър със споделена тайна.
- Инкременталните синхронизации на контакти използват
modifiedSince, а не обхождане на целия списък.
Изграждането и поддръжката на този слой е истинска инженерна работа: проверка на схемата, отстъпление, логика за приемане, изравняване. Tajo съществува, за да я поеме, като поддържа данните от Shopify и търговията синхронизирани с контактите, компаниите и събитията в Brevo, без някой да пише на ръка логиката за повторни опити и премахване на дубликати. Ако все пак свързвате всичко сами, ръководството за интеграция с Brevo преминава през решенията за модела на данните, които идват преди кода.
Основни изводи
- API е една REST повърхност на
https://api.brevo.com/v3/, удостоверявана с хедърapi-key, а не с bearer токен. - Ограниченията на заявките са крайно неравномерни: изпращането е практически неограничено, докато повечето други крайни адреси делят 100 заявки на час при стандартните планове.
- Официални SDK има за седем езика, но HTTP повърхността е достатъчно проста, за да я извиквате директно, когато Ви трябват само няколко крайни адреса.
- Пясъчният режим валидира само формата на заявката, така че пазете отделен акаунт за тестване на всичко отвъд изпращанията.
- Отговор 2xx не доказва, че записът е приложен. Недекларираните атрибути се отхвърлят тихо, а upsert към обекти е асинхронен.
- Проектирайте около фиксираните лимити: една компания на домейн, един милион записа на тип обект, никакво групово изтриване за стандартните обекти и филтри по атрибути, които тихо не правят нищо.