Brevo API: praktičan vodič za programere
Vodič kroz Brevo API za programere: autentifikacija, osnovni URL, kontakti, transakcioni imejl, kampanje, CRM objekti, webhookovi, ograničenja broja zahteva i stvarna ograničenja platforme.
Brevo izlaže jedan REST API koji obuhvata transakcione poruke, marketinške kampanje, podatke o kontaktima i CRM zapise. Da prvi zahtev vrati 201 treba vam oko dva minuta. Da dobijete produkcijsku integraciju koja ne gubi podatke u tišini treba znatno duže, jer je nekoliko najvažnijih ograničenja ili nedokumentovano ili je u suprotnosti sa onim što API prijavljuje o sebi.
Ovaj vodič pokriva obe polovine: krajnje tačke, SDK-ove i autentifikaciju koji su vam potrebni prvog dana, i ograničenja platforme oko kojih morate da projektujete rešenje pre nego što ga pustite u rad.
Šta Brevo API pokriva
Sve živi pod jednim hostom i jednom putanjom verzije. Programerska dokumentacija grupiše tu površinu u četiri proizvodne oblasti:
- Poruke: transakcioni imejl, SMS i WhatsApp, uključujući grupna slanja, zakazivanje i aktivnost poruka.
- Marketinška platforma: kontakti, liste, segmenti i imejl kampanje.
- eCommerce: proizvodi, porudžbine i praćenje događaja kupaca.
- Conversations: čet vidžet i programsko upravljanje razgovorima.
Te oblasti dele jedan nalog, jednu bazu kontakata i jedan API ključ. To je praktično i povremeno opasno: skripta napisana prema zamišljenoj test verziji podataka razgovara sa istim kontaktima kojima šaljete kampanje.
Transakciono naspram marketinškog
Ove dve porodice se dovoljno razlikuju u ponašanju da je njihovo mešanje najčešća greška u projektovanju.
| Transakciono | Marketinško | |
|---|---|---|
| Osnovna krajnja tačka | POST /v3/smtp/email | POST /v3/emailCampaigns |
| Adresiranje | Eksplicitni primaoci u zahtevu | listIds ili segmentIds |
| Okidač | Vaša aplikacija, u realnom vremenu | Zakazano ili poslato na zahtev |
| Tipičan oblik obima | Neprekidno, jedna po jedna poruka | U naletima, jedno veliko slanje |
| Odnos prema ograničenjima | Vrlo visok, 1.000 zahteva u sekundi na standardnim planovima | Nizak, krajnje tačke kampanja spadaju pod opšte ograničenje |
Ako još odlučujete da li je Brevo uopšte prava platforma, pregled platforme pokriva tu temu.
Autentifikacija i upravljanje ključevima
Brevo koristi običan API ključ u prilagođenom zaglavlju. Zaglavlje se zove api-key, ne Authorization, i nema prefiks Bearer. Na tome se saplete skoro svako ko je prvo koristio neki drugi API za poruke.
curl https://api.brevo.com/v3/account \ -H "api-key: $BREVO_API_KEY"Ključevi se generišu u aplikaciji Brevo, u podešavanjima naloga, u odeljku SMTP and API, na kartici API keys. Dajte svakom ključu opisno ime vezano za sistem koji ga koristi. Vrednost ključa prikazuje se tačno jednom prilikom generisanja, pa ako je izgubite, generišete novi umesto da vraćate stari.
Nekoliko praktičnih pravila:
- Izdajte poseban ključ po okruženju i po servisu. Povlačenje kompromitovanog ključa nikada ne bi smelo da obori tri nepovezana sistema.
- Standardni API ključevi važe za ceo nalog. Tretirajte svaki ključ kao pun pristup kontaktima, slanju i CRM podacima.
- Brevo podržava i OAuth 2.0 za aplikacije koje deluju u ime drugih Brevo naloga, što je opisano uz tok sa ključevima u dokumentu authentication schemes.
- MCP server koji koriste AI asistenti uzima poseban token i zaista koristi bearer zaglavlje. Taj token se generiše na istom ekranu sa API ključevima, ali nije zamenljiv sa REST ključem.
Osnovni URL, verzionisanje i vaš prvi upis
Osnovni URL je https://api.brevo.com/v3/. Verzija je u putanji, a ne u zaglavlju, i v3 je aktuelna generacija. Svaka putanja u ovom vodiču data je u odnosu na tu osnovu.
Prvi upis je informativniji od prvog čitanja, jer aktivira one delove naloga koji su obično loše podešeni, pre svega verifikovane pošiljaoce:
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"] }'Uspešno slanje vraća 201 sa poljem messageId. Zakazano slanje vraća 202.
Krajnje tačke koje ćete zaista koristiti
Kontakti
POST /v3/contacts kreira kontakt. Telo prima email, mapu attributes za prilagođena polja, listIds, ext_id za vaš sopstveni spoljni ključ, i dve zastavice koje su u praksi najvažnije: updateEnabled, koja poziv pretvara u upsert, i getId, koja čini da odgovor vrati id kontakta.
Čitanja idu kroz GET /v3/contacts, koji straničari uz limit (podrazumevano 50, najviše 1000) i offset, i podržava modifiedSince i createdSince u UTC vremenu. Inkrementalne sinhronizacije treba da se oslone na modifiedSince umesto da prolaze kroz celu listu. Imajte u vidu da parametar filter podržava samo operator jednakosti, pa sve izražajnije spada u segment.
Za masovno učitavanje, POST /v3/contacts/import prihvata fileUrl, fileBody ili jsonBody, cilja listIds i radi asinhrono, vraćajući processId. Brevo dokumentuje maksimum od 10 MB za telo i preporučuje da ostanete oko 8 MB, jer parsiranje uvećava sadržaj. Prosledite notifyUrl da biste saznali ishod umesto da anketirate status.
Transakcioni imejl
POST /v3/smtp/email je radni konj. Osim sender, to, subject i htmlContent, vredi znati sledeća polja:
templateIdsaparams, koji zamenjuje ugrađeni sadržaj šablonom platforme Brevo i njegovim zamenama promenljivih. Parametri pojedinačne verzije ograničeni su na 100 KB, a kumulativni na 1000 KB.messageVersions, koji šalje personalizovane varijante u jednom pozivu, sa najviše 99 primalaca po verziji.tags, koje uvek treba da postavite. Oznake se vraćaju u webhook događajima i jedini su jeftin način da povežete događaj isporuke sa delom koda koji ga je proizveo.scheduledAtuzbatchId, za buduća slanja koja ćete možda želeti da otkažete kao grupu.headers, u obliku Title-Case, za prilagođena SMTP zaglavlja.
Jedan zahtev prihvata najviše 2.000 primalaca. Za razliku između ove krajnje tačke i slanja kampanja, vodič za transakcioni imejl daje pogled iz ugla strategije poruka.
Imejl kampanje
POST /v3/emailCampaigns zahteva name i sender, plus tačno jedan izvor sadržaja: htmlContent (najmanje 10 znakova, ispod 1 MB), htmlUrl ili templateId. Publika ide u recipients kao listIds ili segmentIds, a scheduledAt koristi UTC format YYYY-MM-DDTHH:mm:ss.SSSZ. Prateće rute pokrivaju trenutno slanje, slanje testa, izmenu statusa i preuzimanje izveštaja o kampanji.
Kompanije, poslovi i objekti
CRM u platformi Brevo ima dve putanje za upis koje se preklapaju, i ispravan izbor je bitan.
CRM rute su POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} i istovetan skup za poslove. One su sinhrone. PATCH vraća 204 čim je izmena primenjena.
API za objekte je putanja za masovni rad: POST /v3/objects/{object_type}/batch/upsert prima do 1000 zapisa i 1 MB po zahtevu, do 500 atributa po zapisu i do 10 povezanih zapisa po tipu objekta po zapisu. Vraća 202 sa poljem processId, što znači prihvaćeno, a ne primenjeno.
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" } } ] }'Vodič kroz Brevo CRM pokriva model objekata iz ugla operatera.
Zvanični SDK-ovi
Brevo održava klijente u GitHub organizaciji getbrevo:
| Jezik | Repozitorijum |
|---|---|
| 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 klijent se instalira kao @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 klijent se instalira sa pip install brevo-python. Ako ne želite da nosite zavisnost od SDK-a zbog dve krajnje tačke, sirova HTTP površina je dovoljno mala da je pozovete direktno, što vas ujedno štiti od stalnih izmena verzija SDK-a:
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 responsePostoji i MCP server na adresi https://mcp.brevo.com/v1/brevo/mcp za AI asistente, sa autentifikacijom preko bearer tokena generisanog na istom ekranu podešavanja. Koristan je za istraživanje i pitanja o nalogu, ne za produkcijske tokove podataka.
Webhookovi
Webhookovi su način da saznate šta se dogodilo posle slanja. POST /v3/webhooks kreira jedan, sa url, events, type i opciono channel (email ili sms), batched, prilagođenim headers i objektom auth.
Postoje tri tipa webhookova sa različitim rečnicima događaja:
- Transakcioni:
sent,request,delivered,hardBounce,softBounce,blocked,spam,invalid,deferred,click,opened,uniqueOpened,unsubscribed. - Marketinški:
spam,opened,click,hardBounce,softBounce,unsubscribed,listAddition,delivered,contactUpdated,contactDeleted. - Dolazni:
inboundEmailProcessedireply, koji dodatno zahtevajudomain.
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" }'Tri stvari treba dobro postaviti. Prvo, nalog može da drži najviše 40 webhookova preko svih tipova, pa usmeravajte po događaju unutar svog obrađivača umesto da registrujete jednu krajnju tačku po događaju. Drugo, koristite zastavicu batched kada očekujete veliki obim, jer je jedan zahtev sa mnogo događaja daleko jeftiniji za obradu od mnogo zahteva. Treće, zaštitite prijemnu stranu: Brevo objavljuje opsege svojih IP adresa za slanje, a ograničavanje krajnje tačke na te opsege je dokumentovani pristup. Dodajte i sopstvenu deljenu tajnu kroz polje headers kao drugi sloj.
Obrađivači moraju biti idempotentni. Tretirajte id poruke, tip događaja i vremensku oznaku kao ključ za uklanjanje duplikata.
Ograničenja broja zahteva i obrada grešaka
Ograničenja broja zahteva u platformi Brevo postavljaju se po krajnjoj tački i po nivou plana, a raspon između krajnjih tačaka je ogroman.
| Krajnja tačka | Standard | Professional i Enterprise |
|---|---|---|
POST /v3/smtp/email | 1.000 RPS | 2.000 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 | više na planu Enterprise |
GET /v3/smtp/emails | 2 RPS, 7.200 RPH | 3 RPS, 10.800 RPH |
| Sve ostalo | 100 RPH | 200 RPH |
Taj poslednji red je onaj koji boli. Slanje je praktično neograničeno, dok upravljanje kampanjama, čitanja iz CRM-a i većina administrativnih poziva dele budžet od 100 zahteva na sat na standardnim planovima. Naivno punjenje podataka koje pre svakog upisa pročita zapis kompanije potrošiće sat vremena kvote za manje od dva minuta.
Svaki odgovor nosi x-sib-ratelimit-limit, x-sib-ratelimit-remaining i x-sib-ratelimit-reset. Čitajte ih i kod uspeha, ne samo kod neuspeha. Prekoračenje ograničenja vraća 429, a ispravan odgovor je da sačekate interval iz zaglavlja za resetovanje, pa da primenite eksponencijalno odlaganje sa nasumičnim pomerajem.
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;}Ponavljajte 429 i 5xx. Nikada ne ponavljajte 400 ili 409 naslepo, jer oba obično znače da je zahtev pogrešan, a ne prerani, dok 409 posebno traži drugačiju radnju, a ne ponavljanje.
Testiranje bez slanja pošte
Dodajte zaglavlje X-Sib-Sandbox sa vrednošću drop u transakciono slanje. Brevo proverava ispravnost zahteva, vraća 201 sa poljem messageId, ne isporučuje ništa i ne upisuje zapis u imejl dnevnik.
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>" }'Shvatite šta ovo dokazuje, a šta ne. Sandbox režim proverava samo format zahteva. On ne govori ništa o autentifikaciji pošiljaoca, iscrtavanju šablona ili isporučivosti. Držite poseban Brevo nalog za integraciono testiranje svega što dodiruje kontakte ili CRM podatke, jer sandbox režim pokriva slanje, a ne ostatak API-ja.
Ograničenja koja oblikuju dizajn vaše integracije
Ovo su ograničenja koja se pojave tek kada integracija radi nad pravim nalogom pri velikom obimu. Nekoliko njih je u suprotnosti sa onim što API tvrdi o sebi. Nijedno nije pregovaračko, pa je jedini razuman odgovor projektovati rešenje oko njih.
Kompanije zahtevaju domen, i samo jedna kompanija po domenu
GET /v3/crm/attributes/companies prijavljuje svaki atribut kao neobavezan, a referenca za kreiranje kompanije navodi samo name kao obavezno. U praksi, POST /v3/companies bez nepraznog atributa domain vraća 400 sa porukom o nedostajućim obaveznim podrazumevanim atributima. Prazan string pada isto kao i izostavljanje.
Gore od toga, jedinstvenost domena se primenjuje. Druga kompanija na domenu koji je već u upotrebi vraća 409. Za B2B trgovinu to je strukturno pitanje: podružnice koje dele jedan imejl domen kupca ne mogu sve da postoje kao odvojene kompanije u platformi Brevo. I sama sinhronizacija kontakta dovoljna je da se kompanija pojavi na imejl domenu tog kontakta, pa kreiranje može da se sudari sa kompanijom koju niko nije izričito napravio. Ispravan obrađivač na 409 preuzima postojeću kompaniju umesto da pada ili ponavlja poziv.
Nedeklarisani atributi se tiho odbacuju
Ovo je najopasnije ponašanje na platformi, a Brevo ga dokumentuje sasvim otvoreno: ako se atribut pojavi u zahtevu, a nije prethodno definisan u šemi objekta, ništa se ne dešava. Nema greške, nema kreiranja atributa, nema upozorenja.
Odgovor 2xx zato nije dokaz da su vaši podaci sleteli. Pročitajte šemu pre upisa, odbacite sve nedeklarisano u sopstvenom klijentu i odbijte da pokrenete sinhronizaciju čiji atributi ne postoje, umesto da mesec dana upisujete pola zapisa pre nego što neko primeti.
Filteri po atributima se prihvataju i ignorišu
GET /v3/companies?filters[attributes.domain]=... vraća 200 i ignoriše filter. Dva potpuno različita filtera vraćaju iste zapise. Ne postoji način koji radi da se kompanija pronađe po atributu kroz tu rutu.
U kombinaciji sa činjenicom da nefiltrirana lista na velikim nalozima pada u 504 pri svakoj veličini stranice, postojeća kompanija može biti zaista nepronalaziva kroz dokumentovanu putanju. Zaobilaznica je da skenirate GET /v3/objects/company/records sa sort=desc, što je brzo, straničeno i vraća atribute, uz ograničenje na razuman broj stranica. Kompanija koja je upravo izazvala 409 skoro uvek je kreirana trenutak ranije, pa je skeniranje od najnovijih brzo pronalazi.
Milion zapisa po tipu objekta, i nema grupnog brisanja
POST /v3/objects/{type}/batch/upsert vraća 400 čim tip objekta dostigne milion zapisa. Blokira i izmene, ne samo kreiranja: obraćanje postojećem zapisu preko njegovog sopstvenog numeričkog id-a pada isto tako. Cela putanja upisa u objekte zatvara se odjednom.
Vraćanje ispod granice je sporo, jer POST /v3/objects/{type}/batch/delete vraća 403 za standardne tipove objekata kao što je company. Jedina ruta je DELETE /v3/companies/{id}, jedan zapis po pozivu uz otprilike 156 ms. Čišćenje 124.000 zapisa na taj način trajalo je satima sa 20 paralelnih radnika. Pratite broj zapisa po rasporedu umesto da granicu otkrijete kroz neuspelu sinhronizaciju, a izmene velikog obima usmerite kroz PATCH /v3/companies/{id}, koji nema takvo ograničenje.
ext_id je id platforme Brevo, ne vaš
Na zapisima objekata, identifiers.ext_id sadrži sopstveni CRM id kompanije u platformi Brevo, string u stilu Mongo baze. To nije slobodan spoljni ključ. Ako upsert vežete za ext_id postavljen na identifikator vaše platforme, dobićete duplikate umesto podudaranja. Vaš spoljni id pripada zasebnom deklarisanom atributu.
Upsert nad objektima je asinhron, upisi u CRM nisu
batch/upsert vraća 202 i processId, pa se primenjuje kasnije. Nepostojeći id pada asinhrono i i dalje vraća 202 pozivaocu. PATCH /v3/companies/{id} vraća 204 i primenjuje se sinhrono. Ako vaša sinhronizacija prijavljuje uspeh, samo sinhrona putanja zaslužuje tu reč bez naknadnog čitanja.
Kratka lista za proveru integracije
- Odvojeni API ključevi po servisu i po okruženju, sa rotacijom pri promenama u timu.
- Svi upisi idu kroz jednog klijenta koji čita zaglavlja sa ograničenjima i odlaže pozive na 429.
- Šema atributa se proverava pri pokretanju, a sinhronizacija odbija da radi ako njeni atributi nedostaju.
- 409 pri kreiranju kompanije znači preuzimanje, ne ponavljanje.
- Masovne putanje koriste API za objekte zbog propusnosti, a CRM rute za sve što mora da bude potvrđeno.
- Webhookovi su idempotentni, grupisani, ograničeni po IP adresi i nose zaglavlje sa deljenom tajnom.
- Inkrementalne sinhronizacije kontakata koriste
modifiedSince, ne prolaske kroz celu listu.
Izgradnja i održavanje ovog sloja je pravi inženjerski posao: provera šeme, odlaganje pokušaja, logika preuzimanja, usaglašavanje podataka. Tajo postoji da to apsorbuje, održavajući Shopify i podatke o trgovini u sinhronizaciji sa Brevo kontaktima, kompanijama i događajima, bez toga da neko ručno piše logiku ponavljanja i uklanjanja duplikata. Ako to ipak povezujete sami, vodič za Brevo integraciju prolazi kroz izbore u modelu podataka koji prethode kodu.
Ključni zaključci
- API je jedna REST površina na adresi
https://api.brevo.com/v3/, sa autentifikacijom preko zaglavljaapi-key, a ne bearer tokena. - Ograničenja broja zahteva su izuzetno neravnomerna: slanje je praktično neograničeno, dok većina ostalih krajnjih tačaka deli 100 zahteva na sat na standardnim planovima.
- Zvanični SDK-ovi postoje za sedam jezika, ali je HTTP površina dovoljno jednostavna da je pozovete direktno kada vam treba samo nekoliko krajnjih tačaka.
- Sandbox režim proverava samo format zahteva, pa držite poseban nalog za testiranje svega izvan slanja.
- Odgovor 2xx ne dokazuje da je upis primenjen. Nedeklarisani atributi se tiho odbacuju, a upsert nad objektima je asinhron.
- Projektujte rešenje oko fiksnih ograničenja: jedna kompanija po domenu, milion zapisa po tipu objekta, bez grupnog brisanja za standardne objekte, i filteri po atributima koji tiho ne rade ništa.