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.

Featured image for article: Sådan skubber du Excel-kontakter til Brevo med en VBA-makro (og Office Scripts-alternativet)

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:

  1. 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.
  2. 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:

  1. Læs hver række fra det aktive regneark (header-række først, én kontakt pr. række).
  2. Byg et JSON-array formet til Brevos jsonBody-parameter.
  3. POST det til https://api.brevo.com/v3/contacts/import med projektmappens gemte API-nøgle.
  4. Vis en message box med resultatet.

Det er det. ~120 linjer VBA. Nedenfor er det fulde, fungerende modul.

Ark-layout makroen forventer

emailfirstNamelastNamecompanycity
[email protected]JaneDoeAcmeBerlin
[email protected]JohnSmithGlobexParis

email er obligatorisk. Hver anden kolonne bliver en Brevo-kontaktattribut, mappet via kolonneheaderen (med store bogstaver) til attributnavnet. Så firstNameFIRSTNAME, companyCOMPANY. 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 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

Trin 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 tryk F5, 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:

  1. Trigger: Recurrence (hver 1 time), eller manuel knap, eller “When a row is modified”, hvis du vil have ændringsdrevet synk.
  2. Action: Excel Online → Run script, peg den på din projektmappe og scriptet ovenfor. Gem returværdien som contacts.
  3. 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
      }
  4. 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

BehovBedste valg
Knap med ét klik i en projektmappe, dit team allerede åbner dagligtVBA-makro (denne guide, øverste halvdel)
Projektmappe i OneDrive/SharePoint, hourly auto-syncOffice Scripts + Power Automate (kræver Premium til HTTP)
Kun Mac Excel, kan ikke bruge VBAOffice Scripts + Power Automate
Data lever i Google Sheets, ikke ExcelApps Script (gratis, planlagte triggers indbygget)
Engangs-import, får aldrig brug for det igenSave As → CSV og brug CSV-import-scriptet
Masseimport fra fil >10 MBCSV 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.

Yderligere læsning

Frequently Asked Questions

Kan en VBA-makro virkelig kalde Brevos API inde fra Excel?
Ja. VBA kan lave HTTP-forespørgsler via MSXML2.XMLHTTP, som leveres med enhver moderne Windows-installation. Makroen POSTer JSON til api.brevo.com/v3/contacts/import, samme endpoint som et Python- eller Node-script ville ramme. Mac Excel kan også via Office Scripts + Power Automate, men desktop Mac VBA kan ikke (intet MSXML).
Kan jeg køre dette på en tidsplan som Apps Script time triggers?
Ikke fra VBA alene. Excel skal være åben, for at Application.OnTime kan fyre. Til ubemandet tidsplanlagt synk har du to muligheder: (1) Windows Task Scheduler, der åbner projektmappen og kører makroen, eller (2) Office Scripts trigget af et planlagt Power Automate-flow (dækket senere i denne guide).
Er VBA sikkert? Min virksomhed blokerer makroer.
VBA-makroer kører med adgang til filsystem og registry, så at blokere dem som standard er en fornuftig politik. To veje udenom: (1) signér makroen med et code-signing-certifikat og læg projektmappen i en Trusted Location, eller (2) spring VBA helt over og brug Office Scripts (TypeScript, sandboxet, ingen filsystemadgang). Office Scripts er den moderne Microsoft-velsignede vej.
Hvordan står det i forhold til Google Apps Script-tilgangen?
Apps Script er tættere på sæt-og-glem, kører i Googles cloud, planlagte triggers indbygget, ingen Excel/Power Automate-licens. VBA vinder for offline-arbejde og manuel synk med ét klik fra en projektmappe, dit team allerede har åben. Office Scripts + Power Automate er cloud/planlagt-ækvivalenten på Microsoft-siden, men Power Automate kræver ofte en betalt premium-konnektor til udgående HTTP.
Start gratis med Brevo