Brevo API: Ein praxisnaher Leitfaden für Entwickler:innen

Brevo-API-Leitfaden: Authentifizierung, Kontakt-Upserts, Webhooks, Rate Limits und sichere Grenzen für Wiederholungen.

Brevo API

Was ist die kurze Antwort?

Nutze https://api.brevo.com/v3/ mit einem api-key-Header. Für einen Kontakt-Sync brauchst du eine stabile Quell-ID, deklarierte Attribute, eine eindeutige Liste und den Schutz bestehender Abmeldungen. Prüfe das Ergebnis, lies die Rate-Limit-Header, begrenze Wiederholungen bei 429 und behandle Webhooks als authentifizierte Signale, die Deduplizierung und Abgleich brauchen.

Mehr erfahren
Auf dieser Seite

Ein Kontaktimport kann 201 zurückgeben und trotzdem falsch sein: Die Quell-ID fehlt, die falsche Liste wurde gewählt oder eine frühere Abmeldung übergangen. Das sind Entscheidungen über Datenverantwortung, keine HTTP-Details.

Dieser Leitfaden bringt zuerst eine Brevo-Anfrage zum Laufen und zeigt dann die Prüfungen für einen wiederholbaren kleinen Kontakt-Sync.

Was die Brevo API abdeckt

Alles liegt unter einem Host und einem Versionspfad. Die Entwicklerdokumentation gliedert die Oberfläche in vier Produktbereiche:

  • Messaging: transaktionale E-Mails, SMS und WhatsApp, inklusive Batch-Versand, Planung und Nachrichtenaktivität.
  • Marketing-Plattform: Kontakte, Listen, Segmente und E-Mail-Kampagnen.
  • eCommerce: Produkte, Bestellungen und Tracking von Kundenereignissen.
  • Conversations: das Chat-Widget und programmatische Verwaltung von Konversationen.

Diese Bereiche teilen sich ein Konto, eine Kontaktdatenbank und einen API-Schlüssel. Das ist praktisch und gelegentlich gefährlich: Ein Skript, das gegen eine Staging-Vorstellung der Daten geschrieben wurde, spricht mit genau den Kontakten, an die deine Kampagnen gehen.

Transaktional versus Marketing

Die beiden Familien verhalten sich unterschiedlich genug, dass sie zu verwechseln der häufigste Designfehler ist.

TransaktionalMarketing
Primärer EndpunktPOST /v3/smtp/emailPOST /v3/emailCampaigns
AdressierungExplizite Empfänger:innen in der AnfragelistIds oder segmentIds
AuslöserDeine Anwendung, in EchtzeitGeplant oder auf Abruf gesendet
Typisches VolumenprofilKontinuierlich, eine Nachricht nach der anderenStoßweise, ein großer Versand
Haltung beim Rate LimitSehr hoch, 1.000 Anfragen pro Sekunde in der allgemeinen StufeNiedrig, Kampagnen-Endpunkte fallen unter das allgemeine Limit

Falls du noch überlegst, ob Brevo überhaupt die richtige Plattform ist, deckt die Plattform-Übersicht dieses Feld ab.

Authentifizierung und Schlüsselverwaltung

Für Standard-API-Schlüssel nutzt Brevo den eigenen Header api-key; einige Operationen akzeptieren außerdem OAuth-Bearer-Token. Der Header heißt api-key, nicht Authorization, und es gibt kein Bearer-Präfix. Darüber stolpern fast alle, die vorher mit einer anderen Messaging-API gearbeitet haben.

OAuth kann für unterstützte Vorgänge einen Bearer-Token verwenden; einen normalen API-Schlüssel sendest du weiterhin im Header api-key.

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

Schlüssel erzeugst du in der Brevo-App in den Kontoeinstellungen, im Bereich SMTP and API, auf dem Tab API keys. Gib jedem Schlüssel einen sprechenden Namen, der auf das System verweist, das ihn nutzt. Der Wert wird genau einmal beim Erzeugen angezeigt. Wenn du ihn verlierst, erzeugst du einen neuen, statt den alten wiederherzustellen.

Ein paar praktische Regeln:

  • Vergib pro Deployment-Ziel und pro Dienst einen eigenen Schlüssel. Das Widerrufen eines kompromittierten Schlüssels sollte nie drei unbeteiligte Systeme lahmlegen.
  • Normale API-Schlüssel gelten kontoweit. Behandle jeden Schlüssel als Vollzugriff auf Kontakte, Versand und CRM-Daten.
  • Brevo unterstützt außerdem OAuth 2.0 für Anwendungen, die im Namen anderer Brevo-Konten handeln, beschrieben neben dem Schlüsselverfahren in den Authentication schemes.
  • Der MCP-Server, den KI-Assistenten nutzen, verlangt ein eigenes Token und arbeitet tatsächlich mit einem Bearer-Header. Dieses Token erzeugst du im selben Bildschirm für API-Schlüssel, es ist aber nicht mit einem REST-Schlüssel austauschbar.

Basis-URL, Versionierung und dein erster Schreibvorgang

Die Basis-URL ist https://api.brevo.com/v3/. Die Version steht im Pfad statt in einem Header, und v3 ist die aktuelle Generation. Jeder Pfad in diesem Leitfaden ist relativ zu dieser Basis.

Für den ersten Kontakt-Schreibvorgang nutze eine Testadresse unter deiner Kontrolle, eine echte Listen-ID und eine Quell-ID, die bei jedem Lauf gleich bleibt. Lege zuerst das Attribut FNAME in Brevo an. Mit einem echten Schlüssel schreibt dieses Beispiel live:

Terminal window
curl -X POST https://api.brevo.com/v3/contacts \
-H "api-key: $BREVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"ext_id": "test-source-contact-1",
"attributes": { "FNAME": "Dev" },
"listIds": [42],
"updateEnabled": true,
"getId": true
}'

Ersetze 42 durch die ID deiner Testliste. Lies den Kontakt anschließend zurück und prüfe ID, Namen, Listenmitgliedschaft und Sperrstatus. Ein 2xx allein beweist keinen korrekten Sync.

Die Endpunkte, die du wirklich nutzen wirst

Kontakte

POST /v3/contacts legt einen Kontakt an. Der Body akzeptiert email, deklarierte attributes, listIds und ext_id als stabilen Quellschlüssel. Mit updateEnabled: true wird ein bestehender Kontakt aktualisiert. getId: true fordert die überlebende ID an. Brevo dokumentiert diese Felder. Verwende bei jedem Lauf dieselbe ext_id und speichere die Zuordnung zwischen Quell-ID und Brevo-ID für spätere Abgleiche.

Lege vor dem ersten Schreibvorgang fest, welches System jedes Attribut besitzt und welche Liste einwilligende Kontakte erhält. Verwende eine explizite listIds-Angabe. Setze bei normalen Upserts niemals emailBlacklisted: false. Brevo warnt, dass eine Adressänderung bei einem gesperrten Kontakt die Sperre aufhebt. Behandle sie als eigenen, geprüften Vorgang. Prüfe Sperr- und Abmeldestatus vor dem Hinzufügen zu einer Marketing-Liste. Zwischen Lesen und Schreiben bleibt ein kleines Zeitfenster; bei strengen Anforderungen ist vor dem Versand eine letzte Sperrprüfung nötig.

Gelesen wird über GET /v3/contacts, das mit limit (Standard 50, Maximum 1000) und offset blättert und modifiedSince sowie createdSince in UTC unterstützt. Inkrementelle Syncs sollten sich auf modifiedSince stützen, statt die gesamte Liste durchzugehen. Beachte, dass der Parameter filter nur einen Gleichheitsoperator unterstützt. Alles Ausdrucksstärkere gehört in ein Segment.

Für das massenhafte Laden nimmt POST /v3/contacts/import entweder fileUrl, fileBody oder jsonBody entgegen, adressiert listIds, läuft asynchron und gibt eine processId zurück. Brevo dokumentiert maximal 10 MB Body und empfiehlt, nahe bei 8 MB zu bleiben, weil das Parsen die Nutzlast aufbläht. Gib notifyUrl an, damit du das Ergebnis erfährst, statt zu pollen.

Transaktionale E-Mails

POST /v3/smtp/email ist das Arbeitspferd. Neben sender, to, subject und htmlContent lohnen sich diese Felder:

  • templateId mit params, das Inline-Inhalte durch eine Brevo-Vorlage samt Variablenersetzung austauscht. Params pro Version sind auf 100 KB begrenzt, kumulierte Params auf 1000 KB.
  • messageVersions, das personalisierte Varianten in einem Aufruf verschickt, mit bis zu 99 Empfänger:innen pro Version.
  • tags können helfen, Zustellereignisse dem auslösenden Anwendungsablauf zuzuordnen.
  • scheduledAt plus batchId, für künftige Versendungen, die du als Gruppe abbrechen möchtest.
  • headers, in Title-Case, für eigene SMTP-Header.

Eine einzelne Anfrage akzeptiert höchstens 2.000 Empfänger:innen. Zum Unterschied zwischen diesem Endpunkt und dem Kampagnenversand liefert der Leitfaden zu transaktionalen E-Mails die strategische Sicht.

E-Mail-Kampagnen

POST /v3/emailCampaigns verlangt name und sender plus genau eine Inhaltsquelle: htmlContent (mindestens 10 Zeichen, unter 1 MB), htmlUrl oder templateId. Die Zielgruppe steht in recipients als listIds oder segmentIds, und scheduledAt nutzt das UTC-Format YYYY-MM-DDTHH:mm:ss.SSSZ. Begleitende Routen decken sofortiges Senden, Testversand, Statusänderung und den Abruf des Kampagnenberichts ab.

Unternehmen, Deals und Objekte

Das CRM von Brevo hat zwei sich überschneidende Schreibpfade, und die richtige Wahl ist wichtig.

Die CRM-Routen sind POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} und das entsprechende Set für Deals. Sie arbeiten synchron. Ein PATCH liefert 204, sobald die Änderung angewendet ist.

Die Objects-API ist der Massenpfad: POST /v3/objects/{object_type}/batch/upsert nimmt bis zu 1000 Datensätze und 1 MB pro Anfrage, bis zu 500 Attribute pro Datensatz und bis zu 10 Verknüpfungsdatensätze pro Objekttyp und Datensatz. Sie liefert 202 mit einer processId, was angenommen heißt und nicht angewendet.

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" }
}
]
}'

Der Brevo-CRM-Leitfaden behandelt das Objektmodell aus der Sicht der Operator:innen.

Offizielle SDKs

Brevo pflegt Clients unter der GitHub-Organisation getbrevo:

SpracheRepository
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

Der Node-Client wird als @getbrevo/brevo installiert:

Terminal window
npm install @getbrevo/brevo
import { BrevoClient } from "@getbrevo/brevo";
// Reconcile uncertain send outcomes before retrying.
const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY, maxRetries: 0 });
const result = await brevo.transactionalEmails.sendTransacEmail({
subject: "Order confirmed",
htmlContent: "<html><body><p>Thanks for your order.</p></body></html>",
sender: { name: "Acme", email: "[email protected]" },
to: [{ email: "[email protected]", name: "Customer" }],
tags: ["order-confirmation"],
});
console.log("Message ID:", result.messageId);

Der Python-Client wird mit pip install brevo-python installiert. Wenn du für zwei Endpunkte lieber keine SDK-Abhängigkeit mitschleppen möchtest: Die rohe HTTP-Oberfläche ist klein genug, um sie direkt anzusprechen, und das hält dich zusätzlich von SDK-Versionswechseln fern:

Den Python-Client installierst du mit pip install brevo-python. Für wenige Endpunkte kannst du auch direkt HTTP verwenden:

import os
import requests
BASE = "https://api.brevo.com/v3"
HEADERS = {"api-key": os.environ["BREVO_API_KEY"]}
def upsert_consented_contact(source_id, email, first_name, list_id, consent):
if not consent:
raise ValueError("No marketing consent in the source")
if not source_id or not isinstance(list_id, int):
raise ValueError("Stable source ID and numeric list ID required")
# Configure FNAME in Brevo before using this explicit field mapping.
response = requests.post(
f"{BASE}/contacts",
headers=HEADERS,
json={
"email": email,
"ext_id": source_id,
"attributes": {"FNAME": first_name},
"listIds": [list_id],
"updateEnabled": True,
"getId": True,
},
timeout=30,
)
response.raise_for_status()
return response.json().get("id") if response.content else None

Eine erfolgreiche Aktualisierung kann eine leere 204-Antwort liefern. None bedeutet dann: Lies den Kontakt erneut, um seine ID zu erhalten.

Dieses Beispiel setzt voraus, dass der Aufrufer Einwilligung und Brevo-Sperren bereits geprüft hat. Es fordert keine Entsperrung an. Der Code schreibt mit einem echten Schlüssel live; teste ihn vorher mit Mocks. Identitätskonflikte können je nach Kennungen und Endpunkt verschiedene 4xx-Antworten erzeugen. Lies die Antwort und gleiche IDs ab, statt für jeden Kontaktkonflikt 409 anzunehmen.

Webhooks

Über Webhooks erfährst du, was nach einem Versand passiert ist. POST /v3/webhooks legt einen an, mit url, events, type und optional channel (email oder sms), batched, eigenen headers und einem auth-Objekt.

Es gibt drei Webhook-Typen mit jeweils eigenem Ereignis-Vokabular:

  • Transaktional: sent, request, delivered, hardBounce, softBounce, blocked, spam, invalid, deferred, click, opened, uniqueOpened, unsubscribed.
  • Marketing: spam, opened, click, hardBounce, softBounce, unsubscribed, listAddition, delivered, contactUpdated, contactDeleted.
  • Inbound: inboundEmailProcessed, wofür eine domain erforderlich ist.
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"
}'

Für Kontaktänderungen registrierst du einen Marketing-Webhook mit contactUpdated. In Brevos Event-Payload heißt das Ereignis contact_updated. Schütze den Empfänger mit dem auth-Bearer-Token oder eigenen Headern aus Brevos Sicherheitsleitfaden. Prüfe das Token, bevor du das Ereignis verarbeitest, und halte es aus Logs heraus.

Speichere einen Deduplizierungsschlüssel aus Ereignistyp, Kontaktkennung und Ereigniszeitstempel für ein ausreichend langes Wiederholungsfenster. Ein Zeitstempel allein ist nicht eindeutig. Zustellungen können doppelt oder ungeordnet eintreffen. Lies nach einem Ereignis den aktuellen Kontakt und gleiche ihn mit deiner maßgeblichen Quelle ab. Definiere ein Replay-Fenster, verwerfe nach der Wiederherstellung veraltete Ereignisse und führe regelmäßig einen inkrementellen Abgleich aus. Ein Webhook signalisiert eine Änderung; allein macht er noch keinen bidirektionalen Sync. Der Connector-Leitfaden behandelt die ganze Entscheidung.

Rate Limits und Fehlerbehandlung

Die Rate Limits von Brevo gelten pro Endpunkt und pro Tarifstufe, und die Spanne zwischen den Endpunkten ist enorm.

EndpunktGeneral (jeder Tarif)Advanced (Professional und 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 RPH20 RPS, 72.000 RPH
GET /v3/smtp/emails2 RPS, 7.200 RPH3 RPS, 10.800 RPH
Endpunkte ohne eigenes Limit100 RPH200 RPH

Brevo dokumentiert x-sib-ratelimit-limit, x-sib-ratelimit-remaining und x-sib-ratelimit-reset für begrenzte Endpunkte. Lies sie auch bei Erfolg. Bei 429 wartest du bis zum Reset und wiederholst nur innerhalb einer festen Grenze für Versuche und Wartezeit. Der Reset-Wert ist eine Dauer in der Einheit des Zeitfensters, meist Sekunden, kein Unix-Zeitstempel.

async function callBrevo(path, init) {
for (let attempt = 0; attempt < 3; attempt++) {
const response = await fetch(`https://api.brevo.com/v3${path}`, {
...init,
headers: { "api-key": process.env.BREVO_API_KEY, ...init.headers },
});
if (response.status !== 429 || attempt === 2) return response;
const raw = Number(response.headers.get("x-sib-ratelimit-reset"));
const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt;
const delayMs = (seconds + 2 ** attempt) * 1000;
if (delayMs > 60_000) return response;
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}

Wenn die nötige Wartezeit 60 Sekunden überschreitet, gibt die Funktion 429 zurück. Plane den nächsten Versuch später ein.

Dieser Helfer ist nur für Anfragen gedacht, deren Wiederholung du geprüft hast. Bei einem unklaren Sendefehler gleiche zuerst deine Nachrichtenkennung ab, bevor du erneut sendest. Protokolliere Status und Rate-Header, aber weder API-Schlüssel noch Kontaktdaten.

Wiederhole 4xx nicht blind. Die Referenz zum Kontakt-Anlegen nennt 400 und 425; ein Kennungskonflikt muss nicht 409 liefern. Prüfe nach einem 5xx auf einen Schreibvorgang, ob die Änderung bereits angekommen ist.

Welcher Status kam zurück?

  1. 429

    Begrenzt warten und wiederholen

    Nutze die Reset-Dauer und stoppe nach einer festen Zahl von Versuchen.

  2. 5xx nach einem Schreibvorgang

    Prüfe, ob er angekommen ist

    Der Server kann die Änderung vor dem Fehler angewendet haben.

  3. 400

    Korrigiere die Anfrage

    Meist ist die Anfrage falsch.

  4. Kontaktkennung kollidiert

    IDs lesen und abgleichen

    Die Kontakt-API verspricht keinen bestimmten Konfliktstatus.

Testen ohne echten Mailversand

Füge einem transaktionalen Versand den Header X-Sib-Sandbox mit dem Wert drop hinzu. Brevo validiert die Anfrage, liefert 201 mit einer messageId, stellt nichts zu und schreibt kein E-Mail-Log.

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>hi</p>" }'

Sei dir klar darüber, was das beweist und was nicht. Der Sandbox-Modus validiert ausschließlich das Format der Anfrage. Über Absenderauthentifizierung, Vorlagen-Rendering oder Zustellbarkeit sagt er nichts. Halte ein separates Brevo-Konto für Integrationstests bereit, sobald Kontakt- oder CRM-Daten im Spiel sind, denn der Sandbox-Modus deckt den Versand ab und nicht den Rest der API.

Vor einem Kontakt-Sync prüfen

  • Wähle eine stabile Quell-ID für Brevo ext_id und speichere die zurückgegebene Brevo-ID.
  • Prüfe alle Attribute vorab. Brevo kann unbekannte oder falsch typisierte Werte ignorieren; lies einen Testkontakt zurück.
  • Nutze eine explizite Listen-ID und einen maßgeblichen Einwilligungsnachweis. Überspringe gesperrte und abgemeldete Kontakte.
  • Zähle angenommene, angewendete, übersprungene und fehlgeschlagene Datensätze getrennt. Eine 202 bei Batch- oder Objekt-Endpunkten bedeutet zunächst nur Annahme.
  • Nutze Marketing-Webhooks als Signale und gleiche danach den aktuellen Stand ab. Authentifiziere, dedupliziere und begrenze Replay.
  • Beobachte Rate-Header und stoppe nach wenigen Wiederholungen.

Tajo konzentriert sich darauf, Commerce-Daten mit Brevo zu synchronisieren. Wenn du diese Schicht selbst baust, vergleicht der Brevo-Connector-Leitfaden die Anschlusswege und ihre Fehlerbehandlung.

Häufig gestellte Fragen

Wie lautet die Basis-URL der Brevo API?

Alle REST-Aufrufe an Brevo gehen an https://api.brevo.com/v3/. Die Version steht im Pfad, und v3 ist die aktuelle Generation. Ein Endpunkt wie der transaktionale Versand ist also der vollständige Pfad https://api.brevo.com/v3/smtp/email.

Wie authentifizierst du dich bei der Brevo API?

Du schickst deinen Schlüssel in einem HTTP-Header namens api-key. Brevo nutzt für normale API-Schlüssel weder einen Authorization- noch einen Bearer-Header. Schlüssel erzeugst du in den Kontoeinstellungen unter SMTP and API, API keys, und der Wert wird nur ein einziges Mal angezeigt.

Wie hoch sind die Rate Limits der Brevo API?

Die Limits gelten pro Endpunkt und pro Rate-Limit-Stufe. In der allgemeinen Stufe, die jeder Tarif bekommt, erlaubt der transaktionale Sende-Endpunkt 1.000 Anfragen pro Sekunde, die Kontakt-Endpunkte 10 pro Sekunde, und Endpunkte ohne eigenes Limit sind auf 100 Anfragen pro Stunde gedeckelt. Professional- und Enterprise-Tarife können die Stufe Advanced nutzen, Enterprise auch die Stufe Extended. Wer ein Limit überschreitet, bekommt 429 zurück.

Gibt es offizielle SDKs von Brevo?

Ja. Brevo veröffentlicht Clients für Node.js, Python, PHP, Java, C#, Go und Ruby unter der GitHub-Organisation getbrevo. Das Node-Paket heißt @getbrevo/brevo auf npm, das Python-Paket brevo-python auf PyPI.

Wie teste ich die Brevo API, ohne echte E-Mails zu verschicken?

Füge einem transaktionalen Versand den Header X-Sib-Sandbox mit dem Wert drop hinzu. Brevo validiert die Anfrage, liefert 201 mit einer messageId zurück, sendet nichts und schreibt kein E-Mail-Log. Geprüft wird nur das Format der Anfrage, nicht die Zustellbarkeit.

Welche Webhook-Events unterstützt Brevo?

Transaktionale Webhooks decken unter anderem Zustellung, Bounce und Abmeldung ab. Marketing-Webhooks ergänzen contactUpdated und contactDeleted. Für Inbound dokumentiert Brevo inboundEmailProcessed.

Wie schützt ein Kontakt-Upsert eine bestehende Abmeldung?

Nutze eine stabile Quell-ID und nur deklarierte Attribute. Setze bei Routine-Upserts emailBlacklisted nicht auf false. Prüfe den bestehenden Kontakt und deine Einwilligungsquelle, bevor du ihn einer Marketing-Liste hinzufügst.

Reicht ein Webhook für einen bidirektionalen Sync?

Nein. Authentifiziere den Empfänger, dedupliziere Zustellungen und gleiche verpasste oder verspätete Ereignisse mit einer maßgeblichen Quelle ab. Ein Webhook ist ein Änderungssignal, kein Beweis für gleichen Datenstand.

Beantrage frühzeitigen Zugang

Gib deinen Vornamen und deine geschäftliche E-Mail-Adresse ein. Wir senden dir einen Link, mit dem du deine Anfrage für den Tajo-Zugang bestätigst.

Brevo erhalten