Sådan skubber du Excel-kontakter til Brevo med en VBA-makro (og Office Scripts-alternativet)
En fungerende VBA-makro, der poster kontakter fra et Excel-ark til Brevos API med ét klik. Plus hvornår du skal bruge Office Scripts + Power Automate i stedet, og afvejningerne ift. en Google Apps Script-opsætning.
Du har kontakter i Excel, og du vil have dem i Brevo. Det hurtige og beskidte svar er at gemme filen som .csv og importere den, hvilket er fint én gang. Til alt, du gør gentagne gange, ugentlig salgsoverlevering, en projektmappe dit team opdaterer dagligt, en partnerliste der bliver opdateret, vil du have en knap inde i Excel, der laver synken.
Denne guide dækker de to veje, der faktisk giver mening til det:
- En VBA-makro indlejret i projektmappen. Ingen licens, ingen cloud, virker offline, kører i det øjeblik en bruger klikker på en knap. Det rigtige svar for ~80 % af “Excel til Brevo”-tilfældene.
- Office Scripts + Power Automate. TypeScript i stedet for VBA, kører i cloud’en, understøtter planlagte triggers. Det rigtige svar, hvis projektmappen ligger i OneDrive/SharePoint, og du vil have ubemandet synk, men vær opmærksom på Power Automate-licens.
Hvis du leder efter Google Sheets-ækvivalenten, se følgeartiklen om Apps Script. Og hvis du bare vil have en engangs-CSV-import fra et script på din laptop, har CSV-import-guiden versioner i Python, Node.js og cURL.
Hvad makroen gør
Når brugeren klikker på en “Sync to Brevo”-knap på arket:
- Læs hver række fra det aktive regneark (header-række først, én kontakt pr. række).
- Byg et JSON-array formet til Brevos
jsonBody-parameter. - POST det til
https://api.brevo.com/v3/contacts/importmed projektmappens gemte API-nøgle. - Vis en message box med resultatet.
Det er det. ~120 linjer VBA. Nedenfor er det fulde, fungerende modul.
Ark-layout makroen forventer
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email er obligatorisk. Hver anden kolonne bliver en Brevo-kontaktattribut, mappet via kolonneheaderen (med store bogstaver) til attributnavnet. Så firstName → FIRSTNAME, company → COMPANY. Brugerdefinerede attributter (alt ud over standardsættet) skal findes i din Brevo-konto først, definér dem under Contacts → Settings → Contact attributes.
Trin 1: Åbn VBA-editoren
I Excel: tryk Alt + F11. VBA-editoren åbner. I Project-ruden til venstre højreklik på din projektmappe og vælg Insert → Module. En tom Module1 dukker op.
Trin 2: Indsæt den fulde makro
Erstat indholdet af Module1 med dette:
' ===========================================================================' 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 IDPrivate 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 0End 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 = 0End 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 IfEnd 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 = rEnd FunctionTrin 3: Gem projektmappen som .xlsm
VBA-makroer overlever kun i makro-aktiverede projektmapper. Save As → vælg Excel Macro-Enabled Workbook (.xlsm). Det almindelige .xlsx-format fjerner makroer stille, mange mister kode på den måde første gang.
Trin 4: Konfigurér din API-nøgle
Kør ConfigureApiKey én gang. Enten:
- I VBA-editoren klik et sted inde i
ConfigureApiKey-sub’en og trykF5, eller - I Excel Developer → Macros, vælg
ConfigureApiKey, Run.
Indsæt din xkeysib-...-nøgle. Makroen gemmer den som en custom document property inde i selve projektmappen, den ligger ikke i kildekoden, ikke i registret, og rejser med filen (så vær opmærksom: hvis du e-mailer .xlsm til en anden, ryger API-nøglen med).
Hvis du hellere vil lægge nøglen et sted uden for projektmappen, skift lagringen til Windows-registret:
' Replace the body of ConfigureApiKey with:SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")SaveSetting/GetSetting skriver under HKCU\Software\VB and VBA Program Settings\Brevo\Sync, pr-bruger, ikke pr-projektmappe. Brug det, hvis flere projektmapper skal dele én nøgle, eller hvis du ikke vil have nøglen i filen.
Trin 5: Tilføj en knap på arket
Det er det, der gør det til en oplevelse med ét klik for ikke-tekniske brugere.
Insert → Shapes → Rectangle, læg en på arket, mærk den “Sync to Brevo.” Højreklik på formen → Assign Macro → vælg SyncSheetToBrevo. Færdig.
Eller, for en mere poleret UI, tilføj en custom ribbon-fane via Office Custom UI Editor, men til de fleste interne værktøjer er form-som-knap rigeligt.
Trin 6: Kør det
Klik på knappen. Makroen læser rækkerne, batcher dem, poster hver batch til Brevo og viser en summary message box. Brevos import er asynkron, så success-beskeden betyder “Brevo har accepteret batchen”, selve oprettelsen af kontakter sker server-side de næste par sekunder. Du får en summary-e-mail fra Brevo, når den er færdig (medmindre du sætter disableNotification: true).
Almindelige faldgruber
Knappen gør ingenting, og der er ingen fejl. Makroer er deaktiveret. Kig på den gule sikkerhedsbjælke øverst på arket, klik Enable Content. Hvis din organisation blokerer makroer, se trust-center / code-signing-vejen nedenfor.
Compile error: User-defined type not defined. Du er på Mac Excel, som ikke har MSXML2.XMLHTTP. Mac VBA kan ikke lave HTTPS-forespørgsler direkte, brug Office Scripts-vejen nedenfor i stedet.
400 Bad Request fra Brevo uden tydelig årsag. Næsten altid en af: (a) en brugerdefineret attribut i dit ark findes endnu ikke i Brevo, opret den først; (b) JSON-escape-bug, citationstegn eller backslashes i celleværdier, der ikke blev escapet. EscapeJson-funktionen i koden håndterer standardtilfældene, hvis dine data har mærkelige tegn, log payload til Immediate window (Debug.Print payload) og inspicér.
401 Unauthorized. Forkert header. Det er api-key (små bogstaver, bindestreg), ikke Authorization. Makroen bruger den rigtige, men hvis du har kopieret en snippet andre steder fra, dobbelttjek.
Excel fryser på store importer. Makroen kører synkront på UI-tråden. For 50.000+ rækker vil du se Excel hænge i 10 til 30 sekunder, mens den bygger JSON og venter på Brevo. Acceptér det, eller skift MSXML2.XMLHTTP til dens async-variant, men på den skala har du det bedre i Power Automate (næste afsnit).
Når VBA ikke er nok: Office Scripts + Power Automate
VBA kan ikke lave planlagt cloud-synk. Hvis du har brug for:
- Projektmappen, der synker til Brevo hver time uden, at nogen åbner den
- Projektmappen i OneDrive/SharePoint, redigeret fra web
- En IT-afdeling, der forbyder desktop-makroer
…så vil du have Office Scripts (Microsofts cloud-ækvivalent til Apps Script) plus Power Automate (deres planlægnings- og HTTP-lag).
Opdelingen: Office Scripts læser arket og returnerer kontaktdataene. Power Automate tager dataene og POSTer dem til Brevo på en trigger.
Office Script’et (Excel for the web → Automate → New Script):
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;}Power Automate-flowet:
- Trigger: Recurrence (hver 1 time), eller manuel knap, eller “When a row is modified”, hvis du vil have ændringsdrevet synk.
- Action: Excel Online → Run script, peg den på din projektmappe og scriptet ovenfor. Gem returværdien som
contacts. - Action: HTTP (det er Premium-konnektoren, se licensbemærkningen nedenfor).
- Method:
POST - URI:
https://api.brevo.com/v3/contacts/import - Headers:
api-key: xkeysib-...,Content-Type: application/json - Body:
{"jsonBody": @{outputs('Run_script')?['body/result']},"listIds": [42],"updateExistingContacts": true}
- Method:
- Action: Condition → hvis status code ≠ 202, send en Teams/e-mail-alert.
Licens-realitetstjek: HTTP-actionen er en Power Automate Premium-konnektor. På Microsoft 365 Business Basic/Standard-planer får du standard-konnektorerne, men ikke Premium. Den billigste workaround er Power Automate Premium-tilkøbet (~15 USD/bruger/måned ved skrivetidspunkt), eller flyt HTTP til en lille Azure Function, som standard-flowet kan kalde. Hvis du allerede er på E3/E5 med Premium inkluderet, er du klar.
Det er hovedårsagen til, at Apps Script-historien er renere: Apps Scripts UrlFetchApp er gratis og uden begrænsning, mens Microsoft-ækvivalenten lægger netværkskaldet bag en betalt konnektor-tier.
VBA vs Office Scripts vs Apps Script: hvornår vælger du hvad
| Behov | Bedste valg |
|---|---|
| Knap med ét klik i en projektmappe, dit team allerede åbner dagligt | VBA-makro (denne guide, øverste halvdel) |
| Projektmappe i OneDrive/SharePoint, hourly auto-sync | Office Scripts + Power Automate (kræver Premium til HTTP) |
| Kun Mac Excel, kan ikke bruge VBA | Office Scripts + Power Automate |
| Data lever i Google Sheets, ikke Excel | Apps Script (gratis, planlagte triggers indbygget) |
| Engangs-import, får aldrig brug for det igen | Save As → CSV og brug CSV-import-scriptet |
| Masseimport fra fil >10 MB | CSV med fileUrl, se CSV-guiden |
Hvorfor det slår Zapier / no-code-platforme
Til et tilbagevendende Excel til Brevo-job opkræver tredjeparts-automatiseringsværktøjer (Zapier, Make, n8n) pr. opgave og lægger en tredjepart mellem dine data og Brevo. VBA-tilgangen har nul løbende omkostninger, intet tredjeparts-dataflow, og lever inde i filen, når projektmappen flytter, flytter integrationen med. Office Scripts + Power Automate er lignende, men med Microsoft som tredjepart (allerede i din stack, hvis du er på M365).
Hele pointen med Brevos POST /v3/contacts/import-endpoint er, at du ikke har brug for en lim-platform, dine værktøjer ved allerede, hvordan man laver HTTP-forespørgsler.