Excel-Kontakte mit einem VBA-Makro zu Brevo schicken (und die Office-Scripts-Alternative)

Ein funktionierendes VBA-Makro, das Kontakte aus einer Excel-Tabelle per Klick an die Brevo-API postet. Dazu, wann du stattdessen Office Scripts + Power Automate nimmst, und die Trade-offs gegenüber einem Google-Apps-Script-Setup.

Featured image for article: Excel-Kontakte mit einem VBA-Makro zu Brevo schicken (und die Office-Scripts-Alternative)

Du hast Kontakte in Excel und willst sie in Brevo. Die schnelle und schmutzige Antwort ist, die Datei als .csv zu speichern und zu importieren, das ist für einmal okay. Für alles, was du wiederholt machst (wöchentliche Übergabe aus dem Vertrieb, eine Arbeitsmappe, die dein Team täglich aktualisiert, eine Partnerliste, die regelmäßig erneuert wird), willst du einen Button direkt in Excel, der die Synchronisation übernimmt.

Diese Anleitung deckt die zwei Wege ab, die dafür wirklich Sinn ergeben:

  1. Ein VBA-Makro, eingebettet in der Arbeitsmappe – keine Lizenz, keine Cloud, funktioniert offline, läuft in dem Moment, in dem ein Nutzer auf einen Button klickt. Die richtige Antwort für etwa 80 % aller “Excel-zu-Brevo”-Fälle.
  2. Office Scripts + Power Automate – TypeScript statt VBA, läuft in der Cloud, unterstützt geplante Trigger. Die richtige Antwort, wenn die Arbeitsmappe in OneDrive/SharePoint liegt und du unbeaufsichtigte Synchronisation willst, aber achte auf die Power-Automate-Lizenzierung.

Wenn du das Google-Sheets-Äquivalent suchst, schau dir den begleitenden Artikel zu Apps Script an. Und wenn du nur einen einmaligen CSV-Import per Skript von deinem Laptop willst, hat die CSV-Import-Anleitung Versionen für Python, Node.js und cURL.

Was das Makro macht

Wenn die Nutzer:innen auf einen “Sync to Brevo”-Button auf dem Sheet klicken:

  1. Liest jede Zeile aus dem aktiven Arbeitsblatt (Header-Zeile zuerst, ein Kontakt pro Zeile).
  2. Baut ein JSON-Array, geformt für Brevos jsonBody-Parameter.
  3. POSTet es mit dem in der Arbeitsmappe gespeicherten API-Schlüssel an https://api.brevo.com/v3/contacts/import.
  4. Zeigt das Ergebnis in einer Message-Box.

Das war’s. Etwa 120 Zeilen VBA. Unten findest du das vollständige, funktionierende Modul.

Sheet-Layout, das das Makro erwartet

emailfirstNamelastNamecompanycity
[email protected]JaneDoeAcmeBerlin
[email protected]JohnSmithGlobexParis

email ist Pflicht. Jede andere Spalte wird zu einem Brevo-Kontakt-Attribut, zugeordnet über den Spaltenheader (in Großbuchstaben) zum Attributnamen. Also firstNameFIRSTNAME, companyCOMPANY. Benutzerdefinierte Attribute (alles über die Standardmenge hinaus) müssen vorher in deinem Brevo-Konto existieren, lege sie unter Kontakte → Einstellungen → Kontakt-Attribute an.

Schritt 1: VBA-Editor öffnen

In Excel: drück Alt + F11. Der VBA-Editor öffnet sich. Im Projekt-Bereich links rechtsklick auf deine Arbeitsmappe und Einfügen → Modul wählen. Ein leeres Module1 erscheint.

Schritt 2: Vollständiges Makro einfügen

Ersetze den Inhalt von Module1 durch das hier:

' ===========================================================================
' Brevo contact sync for Excel
' Reads the active sheet's rows and POSTs them to Brevo's import API.
' ===========================================================================
Option Explicit
Private Const BREVO_API_BASE As String = "https://api.brevo.com/v3"
Private Const BREVO_LIST_ID As Long = 42 ' <- your Brevo list ID
Private Const BATCH_SIZE As Long = 1000
' --- Public entry points (the ones you assign to ribbon buttons) -----------
Public Sub SyncSheetToBrevo()
Dim apiKey As String
apiKey = GetApiKey()
If apiKey = "" Then
MsgBox "No API key configured. Run ConfigureApiKey first.", _
vbExclamation, "Brevo Sync"
Exit Sub
End If
Dim ws As Worksheet
Set ws = ActiveSheet
Dim emailCol As Long
emailCol = FindEmailColumn(ws)
If emailCol = 0 Then
MsgBox "Sheet must have an 'email' column in row 1.", _
vbExclamation, "Brevo Sync"
Exit Sub
End If
Dim lastRow As Long, lastCol As Long
lastRow = ws.Cells(ws.Rows.Count, emailCol).End(xlUp).Row
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
If lastRow < 2 Then
MsgBox "No contact rows found.", vbInformation, "Brevo Sync"
Exit Sub
End If
Dim contacts As Collection
Set contacts = New Collection
Dim r As Long, c As Long
For r = 2 To lastRow
Dim email As String
email = LCase(Trim(CStr(ws.Cells(r, emailCol).Value)))
If email <> "" And InStr(email, "@") > 0 Then
Dim json As String
json = "{""email"":""" & EscapeJson(email) & """,""attributes"":{"
Dim attrFirst As Boolean
attrFirst = True
For c = 1 To lastCol
If c <> emailCol Then
Dim val As String
val = CStr(ws.Cells(r, c).Value)
If val <> "" Then
If Not attrFirst Then json = json & ","
Dim attrName As String
attrName = UCase(Trim(CStr(ws.Cells(1, c).Value)))
json = json & """" & attrName & """:""" & EscapeJson(val) & """"
attrFirst = False
End If
End If
Next c
json = json & "}}"
contacts.Add json
End If
Next r
If contacts.Count = 0 Then
MsgBox "No valid contact rows found.", vbInformation, "Brevo Sync"
Exit Sub
End If
Dim totalSent As Long
Dim batchNum As Long
Dim okCount As Long, failCount As Long
Dim batchStart As Long
For batchStart = 1 To contacts.Count Step BATCH_SIZE
batchNum = batchNum + 1
Dim batchEnd As Long
batchEnd = batchStart + BATCH_SIZE - 1
If batchEnd > contacts.Count Then batchEnd = contacts.Count
Dim payload As String
payload = "{""jsonBody"":["
Dim i As Long
For i = batchStart To batchEnd
If i > batchStart Then payload = payload & ","
payload = payload & contacts(i)
Next i
payload = payload & "],""listIds"":[" & BREVO_LIST_ID & _
"],""updateExistingContacts"":true,""emptyContactsAttributes"":false}"
Dim ok As Boolean
ok = PostToBrevo(apiKey, payload)
If ok Then
okCount = okCount + 1
totalSent = totalSent + (batchEnd - batchStart + 1)
Else
failCount = failCount + 1
End If
Next batchStart
MsgBox "Sent " & totalSent & " contact(s) in " & batchNum & " batch(es)." _
& vbCrLf & "Successful batches: " & okCount _
& vbCrLf & "Failed batches: " & failCount, _
vbInformation, "Brevo Sync"
End Sub
Public Sub ConfigureApiKey()
Dim key As String
key = InputBox("Paste your Brevo API key (xkeysib-...):", "Brevo API Key")
If key = "" Then Exit Sub
key = Trim(key)
If Left(key, 8) <> "xkeysib-" Then
MsgBox "That doesn't look like a Brevo API key (should start with xkeysib-).", _
vbExclamation, "Brevo API Key"
Exit Sub
End If
On Error Resume Next
ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Delete
On Error GoTo 0
ThisWorkbook.CustomDocumentProperties.Add _
Name:="BrevoApiKey", _
LinkToContent:=False, _
Type:=msoPropertyTypeString, _
Value:=key
ThisWorkbook.Save
MsgBox "API key saved inside the workbook.", vbInformation, "Brevo API Key"
End Sub
' --- Private helpers --------------------------------------------------------
Private Function GetApiKey() As String
On Error Resume Next
GetApiKey = ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Value
On Error GoTo 0
End Function
Private Function FindEmailColumn(ws As Worksheet) As Long
Dim lastCol As Long, c As Long
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
For c = 1 To lastCol
If LCase(Trim(CStr(ws.Cells(1, c).Value))) = "email" Then
FindEmailColumn = c
Exit Function
End If
Next c
FindEmailColumn = 0
End Function
Private Function PostToBrevo(apiKey As String, payload As String) As Boolean
Dim http As Object
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "POST", BREVO_API_BASE & "/contacts/import", False
http.SetRequestHeader "api-key", apiKey
http.SetRequestHeader "Content-Type", "application/json"
http.SetRequestHeader "Accept", "application/json"
http.Send payload
PostToBrevo = (http.Status = 202)
If Not PostToBrevo Then
Debug.Print "Brevo error " & http.Status & ": " & http.responseText
End If
End Function
Private Function EscapeJson(s As String) As String
Dim r As String
r = Replace(s, "\", "\\")
r = Replace(r, """", "\""")
r = Replace(r, vbCrLf, "\n")
r = Replace(r, vbLf, "\n")
r = Replace(r, vbCr, "\n")
r = Replace(r, vbTab, "\t")
EscapeJson = r
End Function

Schritt 3: Arbeitsmappe als .xlsm speichern

VBA-Makros bleiben nur in makrofähigen Arbeitsmappen erhalten. Speichern unter → Excel-Arbeitsmappe mit Makros (.xlsm) wählen. Das einfache .xlsx-Format entfernt Makros stillschweigend, viele verlieren Code beim ersten Mal genau so.

Schritt 4: API-Schlüssel konfigurieren

Führe ConfigureApiKey einmal aus. Entweder:

  • Im VBA-Editor irgendwo in das Sub ConfigureApiKey klicken und F5 drücken, oder
  • In Excel Entwicklertools → Makros, ConfigureApiKey auswählen, Ausführen.

Füg deinen xkeysib-...-Schlüssel ein. Das Makro speichert ihn als benutzerdefinierte Dokumenteigenschaft direkt in der Arbeitsmappe, er steckt nicht im Quellcode, nicht in der Registry und reist mit der Datei (also Vorsicht: wenn du die .xlsm jemandem mailst, geht der API-Schlüssel mit).

Wenn du den Schlüssel lieber außerhalb der Arbeitsmappe ablegen willst, tausche die Speicherung gegen die Windows-Registry:

' Replace the body of ConfigureApiKey with:
SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:
GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")

SaveSetting/GetSetting schreibt unter HKCU\Software\VB and VBA Program Settings\Brevo\Sync, pro Nutzer:in, nicht pro Arbeitsmappe. Nutze das, wenn mehrere Arbeitsmappen einen Schlüssel teilen sollen oder du den Schlüssel nicht in der Datei haben willst.

Schritt 5: Button auf dem Sheet hinzufügen

Das macht es zu einem Ein-Klick-Erlebnis für nicht-technische Nutzer:innen.

Einfügen → Formen → Rechteck, leg eines auf das Sheet, beschrifte es mit “Sync to Brevo.” Rechtsklick auf die Form → Makro zuweisenSyncSheetToBrevo wählen. Fertig.

Oder, für eine polierter UI, füg über den Office Custom UI Editor einen eigenen Ribbon-Tab hinzu, aber für die meisten internen Tools reicht die Form-als-Button.

Schritt 6: Loslegen

Klick den Button. Das Makro liest die Zeilen, batcht sie, postet jeden Batch an Brevo und zeigt eine Zusammenfassungs-Box. Brevos Import ist asynchron, die Erfolgsmeldung bedeutet also “Brevo hat den Batch angenommen”, die eigentliche Kontaktanlage passiert serverseitig in den nächsten Sekunden. Du bekommst von Brevo eine E-Mail-Zusammenfassung, wenn alles durch ist (außer du setzt disableNotification: true).

Häufige Stolpersteine

Der Button macht nichts und es gibt keinen Fehler. Makros sind deaktiviert. Schau auf den gelben Sicherheitsbalken oben am Sheet, klick Bearbeitung aktivieren. Wenn dein Unternehmen Makros blockiert, schau dir den Trust-Center- bzw. Code-Signing-Pfad weiter unten an.

Compile error: User-defined type not defined. Du bist auf Mac Excel, das hat kein MSXML2.XMLHTTP. Mac-VBA kann keine HTTPS-Requests direkt machen, nutze stattdessen den Office-Scripts-Pfad weiter unten.

400 Bad Request von Brevo ohne offensichtlichen Grund. Fast immer einer von zwei Punkten: (a) ein benutzerdefiniertes Attribut in deinem Sheet existiert noch nicht in Brevo, leg es zuerst an; (b) ein JSON-Escaping-Bug, Anführungszeichen oder Backslashes in Zellwerten, die nicht escaped wurden. Die EscapeJson-Funktion im Code deckt die Standardfälle ab. Wenn deine Daten seltsame Zeichen enthalten, log payload ins Direktfenster (Debug.Print payload) und schau rein.

401 Unauthorized. Falscher Header. Es ist api-key (kleingeschrieben, mit Bindestrich), nicht Authorization. Das Makro nutzt den richtigen, aber wenn du irgendwo anders ein Snippet kopiert hast, prüf nach.

Excel friert bei großen Importen ein. Das Makro läuft synchron auf dem UI-Thread. Bei 50.000+ Zeilen wirst du Excel 10 bis 30 Sekunden hängen sehen, während es das JSON baut und auf Brevo wartet. Akzeptier das, oder wechsle MSXML2.XMLHTTP auf seine asynchrone Variante. Bei der Größenordnung bist du allerdings besser in Power Automate aufgehoben (nächster Abschnitt).

Wenn VBA nicht reicht: Office Scripts + Power Automate

VBA kann keine geplante Cloud-Synchronisation. Wenn du brauchst:

  • Die Arbeitsmappe synchronisiert stündlich zu Brevo, ohne dass jemand sie öffnet
  • Die Arbeitsmappe in OneDrive/SharePoint, im Web bearbeitet
  • Eine IT-Abteilung, die Desktop-Makros verbietet

…dann willst du Office Scripts (Microsofts Cloud-Äquivalent zu Apps Script) plus Power Automate (deren Scheduling- und HTTP-Schicht).

Die Aufteilung: Office Scripts liest das Sheet und gibt die Kontaktdaten zurück. Power Automate nimmt diese Daten und POSTet sie auf einen Trigger an Brevo.

Das Office Script (Excel im Web → Automatisieren → Neues Skript):

function main(workbook: ExcelScript.Workbook): {email: string, attributes: Record<string, string>}[] {
const sheet = workbook.getActiveWorksheet();
const range = sheet.getUsedRange();
if (!range) return [];
const values = range.getValues() as string[][];
if (values.length < 2) return [];
const headers = values[0].map(h => String(h).trim());
const emailIdx = headers.findIndex(h => h.toLowerCase() === "email");
if (emailIdx === -1) throw new Error("Sheet must have an 'email' column");
const contacts: {email: string, attributes: Record<string, string>}[] = [];
for (let r = 1; r < values.length; r++) {
const row = values[r];
const email = String(row[emailIdx] ?? "").trim().toLowerCase();
if (!email || !email.includes("@")) continue;
const attributes: Record<string, string> = {};
for (let c = 0; c < headers.length; c++) {
if (c === emailIdx) continue;
const v = row[c];
if (v === null || v === "") continue;
attributes[headers[c].toUpperCase()] = String(v);
}
contacts.push({ email, attributes });
}
return contacts;
}

Der Power-Automate-Flow:

  1. Trigger: Wiederholung (jede Stunde), oder manueller Button, oder “Wenn eine Zeile geändert wird”, wenn du änderungsgesteuerte Synchronisation willst.
  2. Aktion: Excel Online → Skript ausführen, zeig auf deine Arbeitsmappe und das Skript oben. Speichere den Rückgabewert als contacts.
  3. Aktion: HTTP (das ist der Premium-Connector, siehe Lizenzhinweis unten).
    • Methode: POST
    • URI: https://api.brevo.com/v3/contacts/import
    • Header: api-key: xkeysib-..., Content-Type: application/json
    • Body:
      {
      "jsonBody": @{outputs('Run_script')?['body/result']},
      "listIds": [42],
      "updateExistingContacts": true
      }
  4. Aktion: Bedingung, wenn Statuscode ≠ 202, schick eine Teams- oder E-Mail-Warnung.

Lizenz-Realitätscheck: Die HTTP-Aktion ist ein Power-Automate-Premium-Connector. Auf Microsoft 365 Business Basic/Standard hast du die Standard-Connectoren, aber kein Premium. Die günstigste Umgehung ist das Power Automate Premium-Add-on (etwa 15 USD pro Nutzer:in pro Monat, Stand Schreiben), oder die HTTP-Aktion in eine kleine Azure Function verlagern, die der Standard-Flow aufrufen kann. Wenn du ohnehin auf E3/E5 mit Premium drin bist, bist du fertig.

Das ist der Hauptgrund, warum die Apps-Script-Story sauberer ist: Apps Scripts UrlFetchApp ist gratis und uneingeschränkt, während das Microsoft-Äquivalent den Netzwerkaufruf hinter eine bezahlte Connector-Stufe legt.

VBA vs. Office Scripts vs. Apps Script: Wann was wählen

BedarfBeste Option
Ein-Klick-Button in einer Arbeitsmappe, die dein Team täglich offen hatVBA-Makro (diese Anleitung, obere Hälfte)
Arbeitsmappe in OneDrive/SharePoint, stündliche Auto-SyncOffice Scripts + Power Automate (Premium für HTTP nötig)
Nur Mac Excel, kann VBA nicht nutzenOffice Scripts + Power Automate
Daten leben in Google Sheets, nicht ExcelApps Script (gratis, geplante Trigger eingebaut)
Einmaliger Import, danach nie wiederSpeichern unter → CSV und das CSV-Import-Skript nutzen
Bulk-Import aus einer Datei >10 MBCSV mit fileUrl, siehe CSV-Anleitung

Warum das besser ist als Zapier oder andere No-Code-Plattformen

Für einen wiederkehrenden Excel-zu-Brevo-Job berechnen Drittanbieter-Automatisierungstools (Zapier, Make, n8n) pro Task und schieben einen Dritten zwischen deine Daten und Brevo. Der VBA-Ansatz hat null laufende Kosten, keinen Drittanbieter-Datenfluss und lebt in der Datei selbst, wenn die Arbeitsmappe wandert, wandert die Integration mit. Office Scripts + Power Automate ist ähnlich, mit Microsoft als Dritten (schon in deinem Stack, wenn du auf M365 bist).

Der ganze Sinn von Brevos POST /v3/contacts/import ist, dass du keine Klebe-Plattform brauchst, deine Tools wissen schon, wie man HTTP-Requests macht.

Frequently Asked Questions

Kann ein VBA-Makro die Brevo-API wirklich aus Excel heraus aufrufen?
Ja. VBA kann HTTP-Requests über MSXML2.XMLHTTP machen, das ist auf jeder modernen Windows-Installation dabei. Das Makro POSTet JSON an api.brevo.com/v3/contacts/import, denselben Endpoint, den ein Python- oder Node-Skript ansprechen würde. Mac Excel kann das ebenfalls über Office Scripts + Power Automate, aber Desktop-Mac-VBA kann es nicht (kein MSXML).
Kann ich das nach Plan laufen lassen wie Apps-Script-Zeit-Trigger?
Aus VBA allein nicht, Excel muss offen sein, damit Application.OnTime feuert. Für unbeaufsichtigte geplante Synchronisation hast du zwei Optionen: (1) Windows Task Scheduler, der die Arbeitsmappe öffnet und das Makro ausführt, oder (2) Office Scripts, getriggert von einem Power-Automate-Recurrence-Flow (wird später in dieser Anleitung behandelt).
Ist VBA sicher? Mein Unternehmen blockiert Makros.
VBA-Makros laufen mit Zugriff auf das Dateisystem und die Registry, sie standardmäßig zu blockieren ist also eine sinnvolle Policy. Zwei Wege drumherum: (1) das Makro mit einem Code-Signing-Zertifikat signieren und die Arbeitsmappe in einen Trusted Location legen, oder (2) VBA komplett überspringen und Office Scripts nutzen (TypeScript, sandboxed, kein Dateisystem-Zugriff). Office Scripts ist der moderne, von Microsoft empfohlene Weg.
Wie schlägt sich das gegen den Google-Apps-Script-Ansatz?
Apps Script ist näher an Set-and-Forget, läuft in Googles Cloud, geplante Trigger sind eingebaut, keine Excel- oder Power-Automate-Lizenz nötig. VBA gewinnt bei Offline-Arbeit und manueller Ein-Klick-Synchronisation aus einer Arbeitsmappe, die dein Team ohnehin offen hat. Office Scripts + Power Automate ist das Cloud- und Scheduling-Äquivalent auf Microsoft-Seite, aber Power Automate braucht für ausgehende HTTP-Calls oft einen kostenpflichtigen Premium-Connector.
Kostenlos mit Brevo starten