Jak wysłać kontakty Excel do Brevo za pomocą makra VBA (i alternatywa Office Scripts)
Działające makro VBA, które wysyła kontakty z arkusza Excel do API Brevo jednym kliknięciem, plus kiedy zamiast tego użyć Office Scripts + Power Automate, oraz porównanie z konfiguracją Google Apps Script.
Masz kontakty w Excelu i chcesz je w Brevo. Szybko-i-brudna odpowiedź to zapisanie pliku jako .csv i import, co jest w porządku raz. Dla wszystkiego, co robisz wielokrotnie (cotygodniowe przekazanie sprzedaży, skoroszyt, który Twój zespół aktualizuje codziennie, lista partnerów, która jest odświeżana), chcesz mieć przycisk wewnątrz Excela, który wykonuje synchronizację.
Ten przewodnik omawia dwie ścieżki, które naprawdę mają sens dla tego:
- Makro VBA osadzone w skoroszycie. Bez licencji, bez chmury, działa offline, uruchamia się w momencie kliknięcia przycisku przez użytkownika. Właściwa odpowiedź dla około 80% przypadków “Excel-do-Brevo”.
- Office Scripts + Power Automate. TypeScript zamiast VBA, działa w chmurze, obsługuje wyzwalacze zaplanowane. Właściwa odpowiedź, jeśli skoroszyt znajduje się w OneDrive/SharePoint i chcesz nienadzorowanej synchronizacji, ale uważaj na licencjonowanie Power Automate.
Jeśli szukasz odpowiednika Google Sheets, zobacz towarzyszący artykuł o Apps Script. A jeśli chcesz po prostu jednorazowy import CSV ze skryptu na laptopie, przewodnik importu CSV ma wersje w Pythonie, Node.js i cURL.
Co robi makro
Gdy użytkownik kliknie przycisk “Sync to Brevo” na arkuszu:
- Odczytuje każdy wiersz aktywnego arkusza (najpierw wiersz nagłówka, jeden kontakt na wiersz).
- Buduje tablicę JSON ukształtowaną dla parametru
jsonBodyBrevo. - POST-uje ją do
https://api.brevo.com/v3/contacts/importz kluczem API zapisanym w skoroszycie. - Pokazuje okno komunikatu z wynikiem.
Tyle. Około 120 linii VBA. Poniżej pełny, działający moduł.
Układ arkusza, którego oczekuje makro
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email jest obowiązkowy. Każda inna kolumna staje się atrybutem kontaktu Brevo, mapowanym przez nagłówek kolumny (wielkimi literami) do nazwy atrybutu. Więc firstName → FIRSTNAME, company → COMPANY. Niestandardowe atrybuty (wszystko poza standardowym zestawem) muszą najpierw istnieć w Twoim koncie Brevo. Zdefiniuj je w Contacts → Settings → Contact attributes.
Krok 1: Otwórz edytor VBA
W Excelu: naciśnij Alt + F11. Otwiera się edytor VBA. W panelu Project po lewej kliknij prawym przyciskiem myszy swój skoroszyt i wybierz Insert → Module. Pojawi się pusty Module1.
Krok 2: Wklej pełne makro
Zastąp zawartość Module1 tym:
' ===========================================================================' 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 FunctionKrok 3: Zapisz skoroszyt jako .xlsm
Makra VBA są zachowywane tylko w skoroszytach z włączoną obsługą makr. Save As, wybierz Excel Macro-Enabled Workbook (.xlsm). Zwykły format .xlsx po cichu usuwa makra. Wiele osób za pierwszym razem traci kod w ten sposób.
Krok 4: Skonfiguruj swój klucz API
Uruchom ConfigureApiKey raz. Albo:
- W edytorze VBA kliknij gdziekolwiek wewnątrz subprocedury
ConfigureApiKeyi naciśnijF5, lub - W Excelu, Developer → Macros, wybierz
ConfigureApiKey, Run.
Wklej swój klucz xkeysib-.... Makro przechowuje go jako niestandardową właściwość dokumentu wewnątrz samego skoroszytu. Nie jest w kodzie źródłowym, nie jest w rejestrze i podróżuje z plikiem (więc bądź świadomy: jeśli wyślesz .xlsm e-mailem do kogoś, klucz API idzie z nim).
Jeśli wolisz umieścić klucz poza skoroszytem, zmień przechowywanie na rejestr Windows:
' Replace the body of ConfigureApiKey with:SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")SaveSetting/GetSetting zapisuje pod HKCU\Software\VB and VBA Program Settings\Brevo\Sync. Per użytkownik, nie per skoroszyt. Użyj tego, jeśli wiele skoroszytów ma dzielić jeden klucz, lub jeśli nie chcesz klucza w pliku.
Krok 5: Dodaj przycisk na arkuszu
To zamienia to w doświadczenie jednego kliknięcia dla użytkowników nietechnicznych.
Insert → Shapes → Rectangle, upuść jeden na arkuszu, nazwij go “Sync to Brevo.” Kliknij prawym przyciskiem myszy kształt → Assign Macro → wybierz SyncSheetToBrevo. Gotowe.
Lub, dla bardziej dopracowanego UI, dodaj niestandardową kartę wstążki przez Office Custom UI Editor. Ale dla większości narzędzi wewnętrznych kształt-jako-przycisk wystarczy.
Krok 6: Uruchom to
Kliknij przycisk. Makro odczytuje wiersze, batchuje je, wysyła każdy batch do Brevo i pokazuje podsumowujące okno komunikatu. Import Brevo jest asynchroniczny, więc komunikat o sukcesie oznacza “Brevo zaakceptowało batch”. Faktyczne tworzenie kontaktów odbywa się po stronie serwera w ciągu kilku następnych sekund. Otrzymasz e-mail z podsumowaniem od Brevo, gdy się skończy (chyba że ustawisz disableNotification: true).
Częste pułapki
Przycisk nic nie robi i nie ma błędu. Makra są wyłączone. Spójrz na żółty pasek bezpieczeństwa u góry arkusza, kliknij Enable Content. Jeśli Twoja organizacja blokuje makra, zobacz ścieżkę trust-center / code-signing poniżej.
Compile error: User-defined type not defined. Jesteś na Mac Excel, który nie ma MSXML2.XMLHTTP. Mac VBA nie może wykonywać żądań HTTPS bezpośrednio; zamiast tego użyj ścieżki Office Scripts poniżej.
400 Bad Request od Brevo bez oczywistej przyczyny. Prawie zawsze jeden z: (a) niestandardowy atrybut w Twoim arkuszu nie istnieje jeszcze w Brevo, najpierw go utwórz; (b) bug escape’owania JSON, cudzysłowy lub backslashe w wartościach komórek, które nie zostały zaescape’owane. Funkcja EscapeJson w kodzie obsługuje standardowe przypadki; jeśli Twoje dane mają dziwne znaki, zaloguj payload do okna Immediate (Debug.Print payload) i sprawdź.
401 Unauthorized. Zły nagłówek. To api-key (małe litery, łącznik), a nie Authorization. Makro używa właściwego, ale jeśli skopiowałeś snippet skądinąd, sprawdź dwukrotnie.
Excel zamarza na dużych importach. Makro działa synchronicznie na wątku UI. Dla 50 000+ wierszy będziesz patrzeć, jak Excel zawisa na 10 do 30 sekund, podczas gdy buduje JSON i czeka na Brevo. Albo to zaakceptuj, albo przełącz MSXML2.XMLHTTP na jego wariant async. Ale na tej skali jesteś lepiej w Power Automate (następna sekcja).
Kiedy VBA nie wystarcza: Office Scripts + Power Automate
VBA nie potrafi zaplanowanej synchronizacji w chmurze. Jeśli potrzebujesz:
- Skoroszyt synchronizujący się z Brevo co godzinę bez otwierania go przez kogokolwiek
- Skoroszyt w OneDrive/SharePoint, edytowany z poziomu webu
- Dział IT, który zabrania makr desktopowych
…wtedy chcesz Office Scripts (chmurowy odpowiednik Apps Script od Microsoft) plus Power Automate (ich warstwę planowania i HTTP).
Podział: Office Scripts odczytuje arkusz i zwraca dane kontaktów. Power Automate bierze te dane i POST-uje je do Brevo na wyzwalacz.
Office Script (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;}Flow Power Automate:
- Trigger: Recurrence (co 1 godzinę), albo ręczny przycisk, albo “When a row is modified”, jeśli chcesz synchronizacji wyzwalanej zmianami.
- Action: Excel Online → Run script, wskaż go na swój skoroszyt i powyższy skrypt. Zapisz jego wartość zwrotną jako
contacts. - Action: HTTP (to konektor Premium, zobacz notę o licencjonowaniu poniżej).
- 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 → jeśli kod statusu ≠ 202, wyślij alert Teams/email.
Sprawdzenie rzeczywistości licencjonowania: akcja HTTP to konektor Power Automate Premium. Na planach Microsoft 365 Business Basic/Standard otrzymujesz standardowe konektory, ale nie Premium. Najtańszy obejście to dodatek Power Automate Premium (około 15 USD/użytkownik/miesiąc w czasie pisania) lub przeniesienie HTTP do małej Azure Function, którą może wywołać standardowy flow. Jeśli już jesteś na E3/E5 z Premium w cenie, masz to ustawione.
To jest główny powód, dla którego historia Apps Script jest czystsza: UrlFetchApp Apps Script jest darmowy i nieograniczony, podczas gdy odpowiednik Microsoft umieszcza wywołanie sieciowe za płatną warstwą konektora.
VBA kontra Office Scripts kontra Apps Script: kiedy co wybrać
| Potrzeba | Najlepsza opcja |
|---|---|
| Przycisk jednego kliknięcia w skoroszycie, który Twój zespół już codziennie otwiera | Makro VBA (ten przewodnik, górna połowa) |
| Skoroszyt w OneDrive/SharePoint, godzinna automatyczna synchronizacja | Office Scripts + Power Automate (potrzebny Premium dla HTTP) |
| Tylko Mac Excel, nie można użyć VBA | Office Scripts + Power Automate |
| Dane żyją w Google Sheets, nie Excelu | Apps Script (darmowy, wbudowane wyzwalacze zaplanowane) |
| Jednorazowy import, nigdy więcej niepotrzebny | Save As → CSV i użyj skryptu importu CSV |
| Masowy import z pliku >10 MB | CSV z fileUrl, zobacz przewodnik CSV |
Dlaczego to bije Zapier / platformy no-code
Dla powtarzającego się zadania Excel-do-Brevo zewnętrzne narzędzia automatyzacji (Zapier, Make, n8n) liczą za zadanie i umieszczają trzecią stronę między Twoimi danymi a Brevo. Podejście VBA ma zerowe bieżące koszty, brak przepływu danych przez trzecią stronę i żyje wewnątrz pliku. Gdy skoroszyt się przemieszcza, integracja przemieszcza się z nim. Office Scripts + Power Automate są podobne, ale z Microsoft jako trzecią stroną (już w Twoim stacku, jeśli jesteś na M365).
Cały sens endpointu POST /v3/contacts/import Brevo polega na tym, że nie potrzebujesz platformy klejącej. Twoje narzędzia już wiedzą, jak wykonywać żądania HTTP.