Brevo API: En praktisk guide til udviklere

Brevo API-guide til udviklere: autentificering, base-URL, kontakter, transaktionel e-mail, kampagner, CRM-objekter, webhooks, rate limits og grænser fra virkeligheden.

Brevo API
Brevo API?

Brevo udstiller ét REST-API, der spænder over transaktionelle beskeder, marketingkampagner, kontaktdata og CRM-poster. Det tager cirka to minutter at få den første forespørgsel til at returnere 201. Det tager betydeligt længere at få en produktionsintegration, der ikke taber data i stilhed, fordi flere af de vigtigste begrænsninger enten er udokumenterede eller modsiger det, API’et selv fortæller om sig selv.

Denne guide dækker begge halvdele: de endpoints, SDK’er og den autentificering, du skal bruge på dag ét, og de platformsgrænser, du skal designe udenom, før du sender i produktion.

Hvad Brevo API dækker

Alt ligger under ét enkelt host og én versionssti. Udviklerdokumentationen opdeler fladen i fire produktområder:

  • Beskeder: transaktionel e-mail, SMS og WhatsApp, inklusive batchafsendelser, planlægning og beskedaktivitet.
  • Marketingplatform: kontakter, lister, segmenter og e-mail-kampagner.
  • eCommerce: produkter, ordrer og sporing af kundehændelser.
  • Conversations: chatwidgetten og programmatisk styring af samtaler.

De områder deler én konto, én kontaktdatabase og én API-nøgle. Det er praktisk og af og til farligt: et script skrevet ud fra en staging-forestilling om dataene taler med præcis de kontakter, dine kampagner sender til.

Transaktionel kontra marketing

De to familier opfører sig forskelligt nok til, at det er den mest almindelige designfejl at blande dem sammen.

TransaktionelMarketing
Primært endpointPOST /v3/smtp/emailPOST /v3/emailCampaigns
AdresseringEksplicitte modtagere i forespørgslenlistIds eller segmentIds
TriggerDin applikation, i realtidPlanlagt eller sendt på kommando
Typisk volumenformKontinuerlig, én besked ad gangenStødvis, én stor afsendelse
Rate limit-positionMeget høj, 1.000 forespørgsler i sekundet på standardplanerLav, kampagne-endpoints falder ind under det generelle loft

Er du stadig i gang med at beslutte, om Brevo overhovedet er den rigtige platform, dækker platformsoverblikket det terræn.

Autentificering og nøglehåndtering

Brevo bruger en almindelig API-nøgle i en brugerdefineret header. Headeren hedder api-key, ikke Authorization, og der er intet Bearer-præfiks. Det snubler næsten alle over, som har brugt et andet beskeds-API først.

Terminal window
curl https://api.brevo.com/v3/account \
-H "api-key: $BREVO_API_KEY"

Nøgler oprettes i Brevo-appen under kontoindstillinger, i afsnittet SMTP and API, på fanen API keys. Giv hver nøgle et sigende navn knyttet til det system, der bruger den. Nøgleværdien vises præcis én gang, når den oprettes, så mister du den, opretter du en ny i stedet for at genfinde den gamle.

Et par praktiske regler:

  • Udsted en separat nøgle pr. deployment-miljø og pr. service. At tilbagekalde en kompromitteret nøgle bør aldrig lægge tre urelaterede systemer ned.
  • Almindelige API-nøgler gælder hele kontoen. Betragt enhver nøgle som fuld adgang til kontakter, afsendelse og CRM-data.
  • Brevo understøtter også OAuth 2.0 til applikationer, der handler på vegne af andre Brevo-konti, beskrevet sammen med nøgleflowet i authentication schemes.
  • MCP-serveren, som AI-assistenter bruger, tager et separat token og bruger faktisk en bearer-header. Det token oprettes på den samme API keys-skærm, men kan ikke bruges i stedet for en REST-nøgle.

Base-URL, versionering og din første skrivning

Base-URL’en er https://api.brevo.com/v3/. Versionen ligger i stien frem for i en header, og v3 er den aktuelle generation. Alle stier i denne guide er relative til den base.

En første skrivning siger mere end en første læsning, fordi den afprøver de dele af kontoen, der som regel er forkert sat op, især verificerede afsendere:

Terminal window
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": "Første transaktionelle afsendelse",
"htmlContent": "<html><body><p>Det virker.</p></body></html>",
"tags": ["smoke-test"]
}'

En vellykket afsendelse returnerer 201 med et messageId. En planlagt afsendelse returnerer 202.

De endpoints, du faktisk kommer til at bruge

Kontakter

POST /v3/contacts opretter en kontakt. Bodyen tager email, et attributes-map til brugerdefinerede felter, listIds, ext_id til din egen eksterne nøgle og de to flag, der betyder mest i praksis: updateEnabled, som gør kaldet til en upsert, og getId, som får svaret til at returnere kontaktens id.

Læsninger går gennem GET /v3/contacts, som pagineres med limit (standard 50, maksimum 1000) og offset, og understøtter modifiedSince og createdSince i UTC. Inkrementelle synkroniseringer bør læne sig op ad modifiedSince frem for at gennemløbe hele listen. Bemærk, at parameteren filter kun understøtter en lighedsoperator, så alt mere udtryksfuldt hører hjemme i et segment.

Til masseindlæsning accepterer POST /v3/contacts/import enten fileUrl, fileBody eller jsonBody, rammer listIds og kører asynkront med et processId som svar. Brevo dokumenterer en maksimal body på 10 MB og anbefaler at holde sig omkring 8 MB, fordi parsingen puster payloaden op. Angiv notifyUrl, så du får resultatet at vide i stedet for at polle.

Transaktionel e-mail

POST /v3/smtp/email er arbejdshesten. Ud over sender, to, subject og htmlContent er disse felter værd at kende:

  • templateId med params, som erstatter indbygget indhold med en Brevo-skabelon og dens variabelsubstitutioner. Params pr. version er begrænset til 100 KB, samlet til 1000 KB.
  • messageVersions, som sender personaliserede varianter i ét kald, med op til 99 modtagere pr. version.
  • tags, som du altid bør sætte. Tags kommer tilbage på webhook-events og er den eneste billige måde at koble en leveringshændelse sammen med den kodesti, der udløste den.
  • scheduledAt plus batchId, til fremtidige afsendelser, du måske vil annullere samlet.
  • headers, i Title-Case, til brugerdefinerede SMTP-headere.

En enkelt forespørgsel accepterer højst 2.000 modtagere. Forskellen mellem dette endpoint og kampagneafsendelse ser du fra strategisiden i guiden til transaktionel e-mail.

E-mail-kampagner

POST /v3/emailCampaigns kræver name og sender plus præcis én indholdskilde: htmlContent (mindst 10 tegn, under 1 MB), htmlUrl eller templateId. Målgruppen sættes i recipients som listIds eller segmentIds, og scheduledAt bruger UTC-formatet YYYY-MM-DDTHH:mm:ss.SSSZ. Tilhørende ruter dækker afsendelse med det samme, testafsendelse, statusopdatering og udtræk af kampagnerapporten.

Virksomheder, deals og objekter

Brevos CRM har to overlappende skrivestier, og det betyder noget, at du vælger rigtigt.

CRM-ruterne er POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} og det tilsvarende sæt til deals. De er synkrone. En PATCH returnerer 204, når ændringen er anvendt.

Objekt-API’et er bulkstien: POST /v3/objects/{object_type}/batch/upsert tager op til 1000 poster og 1 MB pr. forespørgsel, op til 500 attributter pr. post og op til 10 associationsposter pr. objekttype pr. post. Den returnerer 202 med et processId, hvilket betyder modtaget, ikke anvendt.

Terminal window
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-guiden gennemgår objektmodellen fra operatørens side.

Officielle SDK’er

Brevo vedligeholder klienter under GitHub-organisationen getbrevo:

SprogRepository
Node.jsgithub.com/getbrevo/brevo-node
Pythongithub.com/getbrevo/brevo-python
PHPgithub.com/getbrevo/brevo-php
Javagithub.com/getbrevo/brevo-java
C#github.com/getbrevo/brevo-csharp
Gogithub.com/getbrevo/brevo-go
Rubygithub.com/getbrevo/brevo-ruby

Node-klienten installeres som @getbrevo/brevo:

Terminal window
npm install @getbrevo/brevo
import { BrevoClient } from "@getbrevo/brevo";
const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY });
const result = await brevo.transactionalEmails.sendTransacEmail({
subject: "Ordre bekræftet",
htmlContent: "<html><body><p>Tak for din ordre.</p></body></html>",
sender: { name: "Acme", email: "[email protected]" },
to: [{ email: "[email protected]", name: "Customer" }],
tags: ["order-confirmation"],
});
console.log("Message ID:", result.messageId);

Python-klienten installeres med pip install brevo-python. Vil du hellere undgå en SDK-afhængighed for to endpoints, er den rå HTTP-flade lille nok til at kalde direkte, hvilket samtidig holder dig fri af SDK-versionsuro:

import os
import 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

Der findes også en MCP-serverhttps://mcp.brevo.com/v1/brevo/mcp til AI-assistenter, autentificeret med et bearer-token, der oprettes på den samme indstillingsskærm. Den er nyttig til udforskning og kontospørgsmål, ikke til datastier i produktion.

Webhooks

Webhooks er den måde, du finder ud af, hvad der skete efter en afsendelse. POST /v3/webhooks opretter en, med url, events, type og valgfrit channel (email eller sms), batched, brugerdefinerede headers og et auth-objekt.

Der er tre webhook-typer med hver sit hændelsesvokabular:

  • Transaktionel: sent, request, delivered, hardBounce, softBounce, blocked, spam, invalid, deferred, click, opened, uniqueOpened, unsubscribed.
  • Marketing: spam, opened, click, hardBounce, softBounce, unsubscribed, listAddition, delivered, contactUpdated, contactDeleted.
  • Indgående: inboundEmailProcessed og reply, som derudover kræver et domain.
Terminal window
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"
}'

Tre ting skal sidde rigtigt. For det første kan en konto højst rumme 40 webhooks på tværs af alle typer, så rut efter hændelse inde i din handler i stedet for at registrere ét endpoint pr. hændelse. For det andet: brug flaget batched, når du forventer volumen, for én forespørgsel med mange hændelser er langt billigere at behandle end mange forespørgsler. For det tredje: beskyt modtageren. Brevo offentliggør sine afsender-IP-intervaller, og at begrænse dit endpoint til de intervaller er den dokumenterede fremgangsmåde. Læg din egen delte hemmelighed på via feltet headers som andet lag.

Handlere skal være idempotente. Brug beskedens id plus hændelsestype plus tidsstempel som deduplikeringsnøgle.

Rate limits og fejlhåndtering

Brevos rate limits gælder pr. endpoint og pr. plantrin, og spredningen mellem endpoints er enorm.

EndpointStandardProfessional og Enterprise
POST /v3/smtp/email1.000 RPS2.000 RPS
POST /v3/transactionalSMS/send150 RPS200 RPS
/v3/contacts/...10 RPS, 36.000 RPH20 RPS, 72.000 RPH
POST /v3/events10 RPS, 36.000 RPHhøjere på Enterprise
GET /v3/smtp/emails2 RPS, 7.200 RPH3 RPS, 10.800 RPH
Alt andet100 RPH200 RPH

Den sidste række er den, der gør ondt. Afsendelse er reelt umålt, mens kampagnestyring, CRM-læsninger og de fleste administrative kald deler et budget på 100 forespørgsler i timen på standardplaner. Et naivt backfill, der læser en virksomhedspost før hver skrivning, opbruger en times kvote på under to minutter.

Hvert svar bærer x-sib-ratelimit-limit, x-sib-ratelimit-remaining og x-sib-ratelimit-reset. Læs dem ved succes, ikke kun ved fejl. Overskrides en grænse, får du 429, og det rigtige svar er at vente det interval, reset-headeren angiver, og derefter bruge eksponentiel backoff med jitter.

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;
}

Prøv igen ved 429 og 5xx. Prøv aldrig 400 eller 409 igen i blinde, for begge betyder som regel, at forespørgslen er forkert snarere end for tidlig, og især en 409 kræver en anden handling frem for en gentagelse.

Test uden at sende mails

Tilføj headeren X-Sib-Sandbox med værdien drop til en transaktionel afsendelse. Brevo validerer forespørgslen, returnerer 201 med et messageId, leverer ingenting og skriver ingen e-maillog.

Terminal window
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>hej</p>" }'

Forstå, hvad det beviser, og hvad det ikke beviser. Sandbox-tilstand validerer kun forespørgslens format. Den siger intet om afsenderautentificering, skabelonrendering eller leveringsevne. Hold en separat Brevo-konto til integrationstest af alt, der rører kontakter eller CRM-data, for sandbox-tilstand dækker afsendelse og ikke resten af API’et.

Grænser, der former dit integrationsdesign

Det er de begrænsninger, som først dukker op, når en integration kører mod en rigtig konto med volumen. Flere af dem modsiger det, API’et siger om sig selv. Ingen af dem er til forhandling, så det eneste fornuftige svar er at designe udenom.

Virksomheder kræver et domæne, og kun én virksomhed pr. domæne

GET /v3/crm/attributes/companies rapporterer alle attributter som ikke påkrævede, og referencen for create-a-company nævner kun name som obligatorisk. I praksis returnerer POST /v3/companies uden en ikke-tom domain-attribut 400 med en besked om manglende obligatoriske standardattributter. En tom streng fejler på samme måde som at udelade den.

Værre endnu: domæneunikhed håndhæves. En virksomhed nummer to på et domæne, der allerede er i brug, returnerer 409. For B2B-handel er det strukturelt: datterselskaber, der deler ét e-maildomæne for indkøb, kan ikke alle eksistere som separate virksomheder i Brevo. At synkronisere en kontakt er også nok til at få en virksomhed til at dukke op på den kontakts e-maildomæne, så en oprettelse kan kollidere med en virksomhed, ingen udtrykkeligt har oprettet. Den rigtige handler overtager den eksisterende virksomhed ved 409 i stedet for at fejle eller prøve igen.

Uerklærede attributter kasseres i stilhed

Det er den farligste adfærd på platformen, og Brevo dokumenterer den lige ud: hvis en attribut optræder i en forespørgsel, men ikke tidligere er defineret i objektets skema, sker der ingenting. Ingen fejl, ingen oprettelse af attributten, ingen advarsel.

Et 2xx-svar er derfor ikke bevis for, at dine data landede. Læs skemaet før du skriver, kassér alt uerklæret i din egen klient, og nægt at køre en synkronisering, hvis dens attributter ikke findes, i stedet for at skrive halve poster i en måned, før nogen opdager det.

Attributfiltre accepteres og ignoreres

GET /v3/companies?filters[attributes.domain]=... returnerer 200 og ignorerer filteret. To helt forskellige filtre returnerer de samme poster. Der findes ingen fungerende måde at slå en virksomhed op på en attribut via den rute.

Kombineret med at den ufiltrerede liste timer ud med 504 på store konti uanset sidestørrelse, kan en eksisterende virksomhed reelt være umulig at finde via den dokumenterede sti. Løsningen er at scanne GET /v3/objects/company/records med sort=desc, som er hurtig, pagineret og returnerer attributter, afgrænset til et fornuftigt antal sider. En virksomhed, der lige har udløst en 409, blev næsten altid oprettet få øjeblikke før, så en scanning med nyeste først finder den hurtigt.

En million poster pr. objekttype, og ingen bulk delete

POST /v3/objects/{type}/batch/upsert returnerer 400, når en objekttype rummer en million poster. Den blokerer opdateringer såvel som oprettelser: at adressere en eksisterende post på dens eget numeriske id fejler på nøjagtig samme måde. Hele skrivestien til objekter lukker på én gang.

Det går langsomt at komme under loftet igen, for POST /v3/objects/{type}/batch/delete returnerer 403 for Brevos standardobjekttyper som company. Den eneste rute er DELETE /v3/companies/{id}, én post pr. kald på omkring 156 ms. At rydde 124.000 poster den vej tog timer med 20 parallelle workere. Overvåg antallet af poster efter en fast plan i stedet for at opdage loftet gennem en fejlet synkronisering, og rut opdateringer med høj volumen gennem PATCH /v3/companies/{id}, som ikke har den grænse.

ext_id er Brevos id, ikke dit

På objektposter rummer identifiers.ext_id Brevos eget CRM-virksomheds-id, en Mongo-agtig streng. Det er ikke en fri ekstern nøgle. At nøgle en upsert på ext_id sat til din platforms identifikator skaber dubletter i stedet for at matche. Dit eksterne id hører hjemme i en erklæret attribut for sig.

Objekt-upserts er asynkrone, CRM-skrivninger er ikke

batch/upsert returnerer 202 og et processId og anvendes senere. Et ikke-eksisterende id fejler asynkront og returnerer stadig 202 til dit kald. PATCH /v3/companies/{id} returnerer 204 og anvendes synkront. Hvis din synkronisering melder succes, er det kun den synkrone sti, der fortjener ordet uden en efterfølgende læsning.

En kort tjekliste til integrationen

  • Separate API-nøgler pr. service og pr. miljø, roteret ved personaleskift.
  • Alle skrivninger går gennem én klient, der læser rate limit-headere og bakker ud ved 429.
  • Attributskemaet verificeres ved opstart, og synkroniseringen nægter at køre, hvis dens attributter mangler.
  • 409 ved oprettelse af virksomhed betyder overtag, ikke prøv igen.
  • Bulkstier bruger objekt-API’et til gennemløb og CRM-ruter til alt, der skal bekræftes.
  • Webhooks er idempotente, batchede, IP-begrænsede og bærer en header med en delt hemmelighed.
  • Inkrementelle kontaktsynkroniseringer bruger modifiedSince, ikke gennemløb af hele listen.

At bygge og vedligeholde det lag er rigtigt ingeniørarbejde: skemaverifikation, backoff, overtagelseslogik, afstemning. Tajo findes for at absorbere det og holde Shopify- og handelsdata synkroniseret med Brevo-kontakter, virksomheder og hændelser, uden at nogen håndskriver logikken til gentagelser og dublethåndtering. Vil du hellere koble det sammen selv, gennemgår Brevo-integrationsguiden de valg om datamodellen, der kommer før koden.

Vigtigste pointer

  • API’et er én REST-flade på https://api.brevo.com/v3/, autentificeret med en api-key-header frem for et bearer-token.
  • Rate limits er vildt ujævne: afsendelse er reelt umålt, mens de fleste andre endpoints deler 100 forespørgsler i timen på standardplaner.
  • Der findes officielle SDK’er til syv sprog, men HTTP-fladen er enkel nok til at kalde direkte, når du kun har brug for få endpoints.
  • Sandbox-tilstand validerer kun forespørgslens format, så hold en separat konto til test af alt andet end afsendelser.
  • Et 2xx-svar beviser ikke, at en skrivning blev anvendt. Uerklærede attributter droppes i stilhed, og objekt-upserts er asynkrone.
  • Design udenom de faste grænser: én virksomhed pr. domæne, en million poster pr. objekttype, ingen bulk delete for standardobjekter, og attributfiltre, der stille og roligt intet gør.

Ofte Stillede Spørgsmål

Hvad er base-URL'en til Brevo API?
Alle Brevo REST-kald går til https://api.brevo.com/v3/. Versionen er en del af stien, og v3 er den aktuelle generation, så et endpoint som transaktionel afsendelse har den fulde sti https://api.brevo.com/v3/smtp/email.
Hvordan autentificerer du dig mod Brevo API?
Du sender din nøgle i en HTTP-header ved navn api-key. Brevo bruger ikke en Authorization- eller Bearer-header til almindelige API-nøgler. Nøgler oprettes under kontoindstillinger, SMTP and API, API keys, og værdien vises kun én gang.
Hvad er rate limits i Brevo API?
Grænserne gælder pr. endpoint og pr. plantrin. På standardkonti tillader endpointet til transaktionel afsendelse 1.000 forespørgsler i sekundet, kontakt-endpoints tillader 10 i sekundet, og alle andre endpoints er begrænset til 100 forespørgsler i timen. Professional og Enterprise får højere trin. Overskrider du en grænse, får du 429.
Har Brevo officielle SDK'er?
Ja. Brevo udgiver klienter til Node.js, Python, PHP, Java, C#, Go og Ruby under GitHub-organisationen getbrevo. Node-pakken hedder @getbrevo/brevo på npm, og Python-pakken hedder brevo-python på PyPI.
Hvordan tester jeg Brevo API uden at sende rigtige e-mails?
Tilføj headeren X-Sib-Sandbox med værdien drop til en transaktionel afsendelse. Brevo validerer forespørgslen, returnerer 201 med et messageId, sender ingenting og skriver ingen e-maillog. Den tjekker kun forespørgslens format, ikke leveringsevnen.
Hvilke webhook-events understøtter Brevo?
Transaktionelle webhooks dækker sent, request, delivered, hardBounce, softBounce, blocked, spam, invalid, deferred, click, opened, uniqueOpened og unsubscribed. Marketing-webhooks tilføjer listAddition, contactUpdated og contactDeleted. Indgående webhooks dækker inboundEmailProcessed og reply. En konto kan højst rumme 40 webhooks i alt.
Kan jeg oprette to virksomheder med samme domæne i Brevo?
Nej. Brevo håndhæver, at domænet er unikt på virksomhedsposter, og returnerer 409 med en fejl om domæneunikhed, hvis du forsøger. Den rigtige adfærd i en integration er at overtage den eksisterende virksomhed frem for at gentage oprettelsen.
Hvorfor returnerede min Brevo API-skrivning success uden at ændre noget?
Skrivninger til objekter og CRM kasserer i stilhed attributter, der ikke er erklæret i objektets skema. Brevo dokumenterer adfærden eksplicit: der rejses ingen fejl, og der oprettes ingen attribut. Læs skemaet først, og verificér skrivningen i stedet for at stole på en 2xx-status.
Findes der en bulk delete i Brevo API?
Ikke til Brevos standardobjekttyper som company. Ruten til batch delete returnerer 403 for dem, så oprydning kører én post ad gangen via DELETE /v3/companies/{id}. Regn med timer, ikke minutter, når du planlægger masseoprydning.

Bed om tidlig adgang til Tajo

Indtast dit fornavn og en e-mailadresse eller et telefonnummer. Vi kontakter dig med oplysninger om adgang til Tajo.

automatisk genkendelse
Få Brevo