Как да изпратите Excel контакти в Brevo с VBA макрос (и алтернативата с Office Scripts)
Работещ VBA макрос, който изпраща контакти от Excel таблица към API-то на Brevo с едно щракване. Плюс кога да използвате Office Scripts + Power Automate и компромисите спрямо настройка с Google Apps Script.
Имате контакти в Excel и искате да са в Brevo. Бързият и не толкова елегантен отговор е да запазите файла като .csv и да го импортирате, което е добре веднъж. За всичко, което правите многократно, седмично предаване от продажби, работна книга, която екипът Ви обновява ежедневно, списък с партньори, който се опреснява, искате бутон точно в Excel, който прави синхронизацията.
Това ръководство разглежда двата подхода, които наистина имат смисъл за това:
- VBA макрос, вграден в работната книга. Без лицензи, без облак, работи офлайн, изпълнява се в момента, в който потребител щракне на бутон. Правилният отговор за около 80% от случаите „Excel към Brevo”.
- Office Scripts + Power Automate. TypeScript вместо VBA, изпълнява се в облака, поддържа планирани тригери. Правилният отговор, ако работната книга се намира в OneDrive/SharePoint и искате автоматична синхронизация. Имайте предвид лицензирането на Power Automate.
Ако търсите еквивалента за Google Sheets, вижте придружаващата статия за Apps Script. А ако просто искате еднократен CSV импорт от скрипт на Вашия лаптоп, ръководството за CSV импорт има версии на Python, Node.js и cURL.
Какво прави макросът
Когато потребителят щракне на бутон „Sync to Brevo” на таблицата:
- Чете всеки ред от активния работен лист (първо заглавния ред, по един контакт на ред).
- Изгражда JSON масив, оформен за параметъра
jsonBodyна Brevo. - POST-ва го към
https://api.brevo.com/v3/contacts/importсъс съхранения API ключ на работната книга. - Показва прозорец със съобщение за резултата.
Това е всичко. Около 120 реда VBA. По-долу е пълният работещ модул.
Оформление на таблицата, което макросът очаква
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email е задължителен. Всяка друга колона става атрибут на контакт в Brevo, мапнат от заглавието на колоната (с главни букви) към името на атрибута. Така firstName → FIRSTNAME, company → COMPANY. Персонализираните атрибути (всичко извън стандартния набор) трябва първо да съществуват във Вашия 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 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 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-ът:
- Trigger: Recurrence (на всеки 1 час), или ръчен бутон, или „When a row is modified”, ако искате синхронизация, задвижвана от промени.
- Action: Excel Online → Run script, насочете го към работната Ви книга и скрипта по-горе. Запазете върнатата му стойност като
contacts. - 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}
- Method:
- 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, не може да се използва VBA | Office Scripts + Power Automate |
| Данните се намират в Google Sheets, не в Excel | Apps Script (безплатно, вградени планирани тригери) |
| Еднократен импорт, никога няма да Ви трябва отново | Save As → CSV и скриптът за CSV импорт |
| Групов импорт от файл >10 MB | CSV с 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 заявки.