Как да изпратите Excel контакти в Brevo с VBA макрос (и алтернативата с Office Scripts)

Работещ VBA макрос, който изпраща контакти от Excel таблица към API-то на Brevo с едно щракване. Плюс кога да използвате Office Scripts + Power Automate и компромисите спрямо настройка с Google Apps Script.

Featured image for article: Как да изпратите Excel контакти в Brevo с VBA макрос (и алтернативата с Office Scripts)

Имате контакти в Excel и искате да са в Brevo. Бързият и не толкова елегантен отговор е да запазите файла като .csv и да го импортирате, което е добре веднъж. За всичко, което правите многократно, седмично предаване от продажби, работна книга, която екипът Ви обновява ежедневно, списък с партньори, който се опреснява, искате бутон точно в Excel, който прави синхронизацията.

Това ръководство разглежда двата подхода, които наистина имат смисъл за това:

  1. VBA макрос, вграден в работната книга. Без лицензи, без облак, работи офлайн, изпълнява се в момента, в който потребител щракне на бутон. Правилният отговор за около 80% от случаите „Excel към Brevo”.
  2. Office Scripts + Power Automate. TypeScript вместо VBA, изпълнява се в облака, поддържа планирани тригери. Правилният отговор, ако работната книга се намира в OneDrive/SharePoint и искате автоматична синхронизация. Имайте предвид лицензирането на Power Automate.

Ако търсите еквивалента за Google Sheets, вижте придружаващата статия за Apps Script. А ако просто искате еднократен CSV импорт от скрипт на Вашия лаптоп, ръководството за CSV импорт има версии на Python, Node.js и cURL.

Какво прави макросът

Когато потребителят щракне на бутон „Sync to Brevo” на таблицата:

  1. Чете всеки ред от активния работен лист (първо заглавния ред, по един контакт на ред).
  2. Изгражда JSON масив, оформен за параметъра jsonBody на Brevo.
  3. POST-ва го към https://api.brevo.com/v3/contacts/import със съхранения API ключ на работната книга.
  4. Показва прозорец със съобщение за резултата.

Това е всичко. Около 120 реда VBA. По-долу е пълният работещ модул.

Оформление на таблицата, което макросът очаква

emailfirstNamelastNamecompanycity
[email protected]JaneDoeAcmeBerlin
[email protected]JohnSmithGlobexParis

email е задължителен. Всяка друга колона става атрибут на контакт в Brevo, мапнат от заглавието на колоната (с главни букви) към името на атрибута. Така firstNameFIRSTNAME, companyCOMPANY. Персонализираните атрибути (всичко извън стандартния набор) трябва първо да съществуват във Вашия Brevo акаунт. Дефинирайте ги под Contacts → Settings → Contact attributes.

Стъпка 1: Отворете VBA редактора

В Excel: натиснете Alt + F11. VBA редакторът се отваря. В панела Project вляво щракнете с десен бутон върху работната си книга и изберете Insert → Module. Появява се празен Module1.

Стъпка 2: Поставете пълния макрос

Заменете съдържанието на Module1 с това:

' ===========================================================================
' 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

Стъпка 3: Запазете работната книга като .xlsm

VBA макросите се запазват само в работни книги с разрешени макроси. Save As → изберете Excel Macro-Enabled Workbook (.xlsm). Обикновеният .xlsx формат премахва макросите тихо. Много хора губят код по този начин при първото използване.

Стъпка 4: Конфигурирайте Вашия API ключ

Изпълнете ConfigureApiKey веднъж. Или:

  • Във VBA редактора щракнете където и да е вътре в sub ConfigureApiKey и натиснете F5, или
  • В Excel Developer → Macros, изберете ConfigureApiKey, Run.

Поставете Вашия xkeysib-... ключ. Макросът го съхранява като персонализирано свойство на документа в самата работна книга. Не е в изходния код, не е в регистъра и пътува с файла (имайте предвид: ако изпратите .xlsm на някого по имейл, API ключът отива с него).

Ако предпочитате да съхранявате ключа извън работната книга, превключете към регистъра на Windows:

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

SaveSetting/GetSetting записва под HKCU\Software\VB and VBA Program Settings\Brevo\Sync, на ниво потребител, не на ниво работна книга. Използвайте това, ако няколко работни книги трябва да споделят един ключ или ако не искате ключът да е във файла.

Стъпка 5: Добавете бутон на таблицата

Това е, което го превръща в опит с едно щракване за нетехнически потребители.

Insert → Shapes → Rectangle, поставете един на таблицата, означете го „Sync to Brevo.” Щракнете с десен бутон на формата → Assign Macro → изберете SyncSheetToBrevo. Готово.

Или, за по-полиран UI, добавете персонализиран ribbon tab чрез Office Custom UI Editor. Но за повечето вътрешни инструменти подходът „форма като бутон” е напълно достатъчен.

Стъпка 6: Изпълнете го

Щракнете на бутона. Макросът чете редовете, групира ги, POST-ва всяка група към Brevo и показва обобщаващо съобщение. Импортът на Brevo е асинхронен, така че съобщението за успех означава „Brevo прие групата”. Самото създаване на контакти се случва на сървъра в следващите няколко секунди. Ще получите обобщителен имейл от Brevo, когато приключи (освен ако не сте задали disableNotification: true).

Често срещани капани

Бутонът не прави нищо и няма грешка. Макросите са деактивирани. Погледнете жълтата лента за сигурност в горната част на таблицата, щракнете Enable Content. Ако организацията Ви блокира макроси, вижте пътя през trust-center / code-signing по-долу.

Compile error: User-defined type not defined. Използвате Mac Excel, който няма MSXML2.XMLHTTP. Mac VBA не може да прави HTTPS заявки директно. Използвайте подхода с Office Scripts по-долу.

400 Bad Request от Brevo без очевидна причина. Почти винаги е едно от: (а) персонализиран атрибут в таблицата Ви все още не съществува в Brevo, създайте го първо; (б) грешка при JSON екраниране, кавички или обратни наклонени черти в стойностите на клетки, които не са били екранирани. Функцията EscapeJson в кода обработва стандартните случаи. Ако данните Ви имат странни знаци, логнете payload в Immediate window (Debug.Print payload) и инспектирайте.

401 Unauthorized. Грешен header. Името е api-key (с малки букви, тире), не Authorization. Макросът използва правилния, но ако сте копирали фрагмент от другаде, проверете отново.

Excel замръзва при големи импорти. Макросът работи синхронно на UI нишката. За 50 000+ реда ще наблюдавате как Excel замръзва за 10 до 30 секунди, докато изгражда JSON и чака Brevo. Или го приемете, или превключете MSXML2.XMLHTTP към асинхронния му вариант, но при този мащаб е по-добре да използвате Power Automate (следващата секция).

Когато VBA не е достатъчен: Office Scripts + Power Automate

VBA не може да прави облачна синхронизация по график. Ако Ви трябва:

  • Работната книга да се синхронизира с Brevo на всеки час, без някой да я отваря
  • Работна книга в OneDrive/SharePoint, редактирана от уеб
  • IT отдел, който забранява десктоп макроси

…тогава искате Office Scripts (облачният еквивалент на Apps Script от Microsoft) плюс Power Automate (техния слой за планиране и HTTP).

Разделението: Office Scripts чете таблицата и връща данните за контактите. Power Automate взема тези данни и ги POST-ва към Brevo при тригер.

Office Script-ът (Excel за уеб → 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 flow-ът:

  1. Trigger: Recurrence (на всеки 1 час), или ръчен бутон, или „When a row is modified”, ако искате синхронизация, задвижвана от промени.
  2. Action: Excel Online → Run script, насочете го към работната Ви книга и скрипта по-горе. Запазете върнатата му стойност като contacts.
  3. Action: HTTP (това е Premium конектор, вижте бележката за лицензиране по-долу).
    • 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 → ако кодът на статуса не е 202, изпратете аларма в Teams/имейл.

Реалност за лицензирането: HTTP действието е Premium конектор на Power Automate. На Microsoft 365 Business Basic/Standard плановете получавате стандартните конектори, но не и Premium. Най-евтиното заобикаляне е добавката Power Automate Premium (около 15 щ.д. на потребител на месец към момента на писане), или преместете HTTP в малка Azure Function, която стандартният flow може да извика. Ако сте на E3/E5 с включен Premium, готови сте.

Това е основната причина историята с Apps Script да е по-чиста: UrlFetchApp на Apps Script е безплатна и неограничена, докато еквивалентът на Microsoft поставя мрежовото извикване зад платен конектор.

VBA срещу Office Scripts срещу Apps Script: кога какво да изберете

НуждаНай-добра опция
Бутон с едно щракване в работна книга, която екипът Ви вече отваря ежедневноVBA макрос (това ръководство, горната половина)
Работна книга в OneDrive/SharePoint, автоматична синхронизация на часOffice Scripts + Power Automate (нужен е Premium за HTTP)
Само Mac Excel, не може да се използва VBAOffice Scripts + Power Automate
Данните се намират в Google Sheets, не в ExcelApps Script (безплатно, вградени планирани тригери)
Еднократен импорт, никога няма да Ви трябва отновоSave As → CSV и скриптът за CSV импорт
Групов импорт от файл >10 MBCSV с fileUrl, вижте ръководството за CSV

Защо това е по-добро от Zapier / no-code платформи

За повтаряща се работа Excel към Brevo, инструменти за автоматизация от трети страни (Zapier, Make, n8n) таксуват на задача и поставят трета страна между данните Ви и Brevo. Подходът с VBA има нулев текущ разход, без поток на данни към трета страна, и живее вътре във файла. Когато работната книга се мести, интеграцията се движи с нея. Office Scripts + Power Automate е подобно, но с Microsoft като третата страна (вече е във Вашия стек, ако сте на M365).

Цялата идея на endpoint-а на Brevo POST /v3/contacts/import е, че не Ви трябва свързваща платформа. Вашите инструменти вече знаят как да правят HTTP заявки.

Допълнително четиво

Frequently Asked Questions

Може ли VBA макрос наистина да извика API-то на Brevo от вътрешността на Excel?
Да. VBA може да прави HTTP заявки чрез MSXML2.XMLHTTP, който се доставя с всяка модерна Windows инсталация. Макросът POST-ва JSON към api.brevo.com/v3/contacts/import, същия endpoint, който би използвал скрипт на Python или Node. Mac Excel също може чрез Office Scripts + Power Automate, но десктоп Mac VBA не може (няма MSXML).
Мога ли да го изпълнявам по график като time triggers на Apps Script?
Не само от VBA. Excel трябва да е отворен, за да задейства Application.OnTime. За автоматична синхронизация по график имате две опции: (1) Windows Task Scheduler, който отваря работната книга и изпълнява макроса, или (2) Office Scripts, задействан от планиран Power Automate flow (разгледано по-долу в това ръководство).
Сигурен ли е VBA? Моята фирма блокира макроси.
VBA макросите се изпълняват с достъп до файловата система и регистъра, така че блокирането им по подразбиране е разумна политика. Два пътя около това: (1) подпишете макроса с code-signing сертификат и поставете работната книга в Trusted Location, или (2) пропуснете VBA напълно и използвайте Office Scripts (TypeScript, в sandbox, без достъп до файловата система). Office Scripts е модерният път, благословен от Microsoft.
Как се сравнява това с подхода Google Apps Script?
Apps Script е по-близо до „настрой и забрави”. Изпълнява се в облака на Google, има вградени планирани тригери, не изисква лиценз за Excel/Power Automate. VBA печели за работа офлайн и ръчна синхронизация с едно щракване от работна книга, която екипът Ви вече има отворена. Office Scripts + Power Automate е облачният еквивалент по график от страна на Microsoft, но Power Automate често изисква платен premium конектор за изходящ HTTP.
Започнете безплатно с Brevo