Come spingere i contatti Excel in Brevo con una macro VBA (e l'alternativa Office Scripts)
Una macro VBA funzionante che posta i contatti da un foglio Excel all'API di Brevo in un clic, oltre a quando usare Office Scripts + Power Automate, e i compromessi rispetto a un setup Google Apps Script.
Hai contatti in Excel e li vuoi in Brevo. La risposta veloce e sporca è salvare il file come .csv e importarlo, va bene una volta. Per qualsiasi cosa che fai ripetutamente (passaggio di consegne settimanale dal commerciale, una cartella di lavoro che il tuo team aggiorna ogni giorno, una lista partner che viene rinfrescata di continuo), vuoi un pulsante dentro Excel che faccia la sincronizzazione.
Questa guida copre le due strade che hanno davvero senso per questo:
- Una macro VBA incorporata nella cartella di lavoro, niente licenze, niente cloud, funziona offline, gira nel momento in cui un utente clicca su un pulsante. La risposta giusta per circa l’80 % dei casi “Excel a Brevo”.
- Office Scripts + Power Automate, TypeScript invece di VBA, gira nel cloud, supporta trigger programmati. La risposta giusta se la cartella di lavoro vive in OneDrive/SharePoint e vuoi sincronizzazione non presidiata, ma stai attento alla licenza Power Automate.
Se cerchi l’equivalente per Google Sheets, vedi l’articolo compagno su Apps Script. E se vuoi solo un’importazione CSV una tantum da uno script sul tuo portatile, la guida all’importazione CSV ha versioni in Python, Node.js e cURL.
Cosa fa la macro
Quando l’utente clicca su un pulsante “Sync to Brevo” sul foglio:
- Legge ogni riga dal foglio attivo (riga di header per prima, un contatto per riga).
- Costruisce un array JSON modellato per il parametro
jsonBodydi Brevo. - Lo POSTa a
https://api.brevo.com/v3/contacts/importcon la API key salvata nella cartella di lavoro. - Mostra un message box con il risultato.
Tutto qui. Circa 120 righe di VBA. Sotto trovi il modulo completo e funzionante.
Layout del foglio che la macro si aspetta
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email è obbligatoria. Ogni altra colonna diventa un attributo del contatto Brevo, mappato dall’header di colonna (in maiuscolo) al nome dell’attributo. Quindi firstName → FIRSTNAME, company → COMPANY. Gli attributi personalizzati (qualsiasi cosa oltre il set standard) devono prima esistere nel tuo account Brevo, definiscili in Contatti → Impostazioni → Attributi del contatto.
Passo 1: aprire l’editor VBA
In Excel: premi Alt + F11. Si apre l’editor VBA. Nel riquadro Progetto a sinistra, clic destro sulla tua cartella di lavoro e scegli Inserisci → Modulo. Compare un Module1 vuoto.
Passo 2: incollare la macro completa
Sostituisci il contenuto di Module1 con questo:
' ===========================================================================' 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 FunctionPasso 3: salvare la cartella di lavoro come .xlsm
Le macro VBA persistono solo in cartelle di lavoro abilitate alle macro. Salva con nome → scegli Cartella di lavoro Excel con attivazione macro (.xlsm). Il formato .xlsx semplice rimuove le macro in silenzio, in tanti perdono codice così la prima volta.
Passo 4: configurare la tua API key
Esegui ConfigureApiKey una volta. O:
- Nell’editor VBA, clicca dentro il sub
ConfigureApiKeye premiF5, oppure - In Excel, Sviluppo → Macro, scegli
ConfigureApiKey, Esegui.
Incolla la tua chiave xkeysib-.... La macro la salva come proprietà personalizzata del documento dentro la cartella di lavoro stessa, non sta nel codice sorgente, non sta nel registro, e viaggia col file (quindi attenzione: se mandi via email l’.xlsm a qualcuno, la API key parte con esso).
Se preferisci mettere la chiave fuori dalla cartella di lavoro, scambia lo storage col registro Windows:
' Replace the body of ConfigureApiKey with:SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")SaveSetting/GetSetting scrive sotto HKCU\Software\VB and VBA Program Settings\Brevo\Sync, per utente, non per cartella di lavoro. Usa questo se più cartelle di lavoro devono condividere una chiave o se non vuoi la chiave nel file.
Passo 5: aggiungere un pulsante sul foglio
Questo è ciò che la trasforma in un’esperienza in un clic per gli utenti non tecnici.
Inserisci → Forme → Rettangolo, poggiane uno sul foglio, etichettalo “Sync to Brevo.” Clic destro sulla forma → Assegna macro → scegli SyncSheetToBrevo. Fatto.
Oppure, per un’interfaccia più curata, aggiungi una scheda della barra multifunzione personalizzata tramite l’Office Custom UI Editor, ma per la maggior parte degli strumenti interni, la forma-come-pulsante basta e avanza.
Passo 6: lanciarla
Clicca il pulsante. La macro legge le righe, le mette in batch, posta ogni batch su Brevo e mostra un message box riassuntivo. L’importazione di Brevo è asincrona, quindi il messaggio di successo significa “Brevo ha accettato il batch”, la creazione vera dei contatti accade lato server nei secondi successivi. Riceverai un email riassuntiva da Brevo quando finisce (a meno che tu non imposti disableNotification: true).
Trappole comuni
Il pulsante non fa niente e non c’è errore. Le macro sono disabilitate. Guarda la barra di sicurezza gialla in cima al foglio, clicca Abilita contenuto. Se la tua organizzazione blocca le macro, vedi la strada del trust center / firma del codice più sotto.
Compile error: User-defined type not defined. Sei su Mac Excel, che non ha MSXML2.XMLHTTP. Il VBA Mac non può fare richieste HTTPS direttamente, usa la strada Office Scripts qui sotto invece.
400 Bad Request da Brevo senza causa ovvia. Quasi sempre una di queste: (a) un attributo personalizzato nel tuo foglio non esiste ancora in Brevo, crealo prima; (b) un bug di escape JSON, virgolette o backslash in valori di cella che non sono stati escapati. La funzione EscapeJson nel codice gestisce i casi standard, se i tuoi dati hanno caratteri strani, logga payload nella finestra immediata (Debug.Print payload) e ispeziona.
401 Unauthorized. Header sbagliato. È api-key (minuscolo, con trattino), non Authorization. La macro usa quello giusto, ma se hai copiato uno snippet da qualche altra parte, ricontrolla.
Excel si congela su importazioni grandi. La macro gira sincrona sul thread dell’interfaccia. Per 50.000 righe e oltre, vedrai Excel impallato per 10-30 secondi mentre costruisce il JSON e aspetta Brevo. O lo accetti, o cambi MSXML2.XMLHTTP con la sua variante asincrona, ma a quella scala stai meglio in Power Automate (sezione successiva).
Quando VBA non basta: Office Scripts + Power Automate
VBA non può fare sincronizzazione cloud programmata. Se ti serve:
- Che la cartella di lavoro si sincronizzi con Brevo ogni ora senza che nessuno la apra
- Che la cartella di lavoro stia in OneDrive/SharePoint, modificata dal web
- Un reparto IT che vieta le macro desktop
…allora vuoi Office Scripts (l’equivalente cloud di Microsoft di Apps Script) più Power Automate (il loro layer di scheduling e HTTP).
La suddivisione: Office Scripts legge il foglio e restituisce i dati dei contatti. Power Automate prende quei dati e li POSTa a Brevo su un trigger.
L’Office Script (Excel sul web → Automatizza → Nuovo 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;}Il flusso Power Automate:
- Trigger: Periodicità (ogni 1 ora), o pulsante manuale, o “Quando una riga viene modificata” se vuoi sincronizzazione guidata dai cambiamenti.
- Azione: Excel Online → Esegui script, puntalo alla tua cartella di lavoro e allo script qui sopra. Salva il valore di ritorno come
contacts. - Azione: HTTP (questo è il connettore Premium, vedi la nota di licenza più sotto).
- Metodo:
POST - URI:
https://api.brevo.com/v3/contacts/import - Header:
api-key: xkeysib-...,Content-Type: application/json - Body:
{"jsonBody": @{outputs('Run_script')?['body/result']},"listIds": [42],"updateExistingContacts": true}
- Metodo:
- Azione: Condizione, se il codice di stato ≠ 202, manda una notifica Teams o email.
Realtà delle licenze: l’azione HTTP è un connettore Premium di Power Automate. Sui piani Microsoft 365 Business Basic/Standard hai i connettori standard ma non Premium. La via di uscita più economica è l’add-on Power Automate Premium (circa 15 USD per utente al mese al momento della scrittura), oppure spostare l’HTTP in una piccola Azure Function che il flusso standard può chiamare. Se sei già su E3/E5 con Premium incluso, sei a posto.
Questo è il motivo principale per cui la storia di Apps Script è più pulita: UrlFetchApp di Apps Script è gratis e senza restrizioni, mentre l’equivalente Microsoft mette la chiamata di rete dietro un livello di connettore a pagamento.
VBA contro Office Scripts contro Apps Script: quando scegliere cosa
| Bisogno | Migliore opzione |
|---|---|
| Pulsante in un clic in una cartella di lavoro che il tuo team apre già ogni giorno | Macro VBA (questa guida, metà superiore) |
| Cartella di lavoro in OneDrive/SharePoint, auto-sync orario | Office Scripts + Power Automate (Premium necessario per HTTP) |
| Solo Mac Excel, non puoi usare VBA | Office Scripts + Power Automate |
| I dati vivono in Google Sheets, non Excel | Apps Script (gratis, trigger programmati integrati) |
| Importazione una tantum, non servirà mai più | Salva con nome → CSV e usa lo script di importazione CSV |
| Importazione in massa da un file >10 MB | CSV con fileUrl, vedi la guida CSV |
Perché questo batte Zapier e le piattaforme no-code
Per un job ricorrente Excel a Brevo, gli strumenti di automazione di terze parti (Zapier, Make, n8n) addebitano per task e mettono una terza parte tra i tuoi dati e Brevo. L’approccio VBA ha zero costi ricorrenti, nessun flusso dati di terzi, e vive dentro il file, quando la cartella di lavoro si sposta, l’integrazione si sposta con essa. Office Scripts + Power Automate è simile ma con Microsoft come terza parte (già nel tuo stack se sei su M365).
L’intero senso dell’endpoint POST /v3/contacts/import di Brevo è che non ti serve una piattaforma di colla, i tuoi strumenti sanno già fare richieste HTTP.