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.

Featured image for article: Come spingere i contatti Excel in Brevo con una macro VBA (e l'alternativa Office Scripts)

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:

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

  1. Legge ogni riga dal foglio attivo (riga di header per prima, un contatto per riga).
  2. Costruisce un array JSON modellato per il parametro jsonBody di Brevo.
  3. Lo POSTa a https://api.brevo.com/v3/contacts/import con la API key salvata nella cartella di lavoro.
  4. 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

emailfirstNamelastNamecompanycity
[email protected]JaneDoeAcmeBerlin
[email protected]JohnSmithGlobexParis

email è obbligatoria. Ogni altra colonna diventa un attributo del contatto Brevo, mappato dall’header di colonna (in maiuscolo) al nome dell’attributo. Quindi firstNameFIRSTNAME, companyCOMPANY. 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 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

Passo 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 ConfigureApiKey e premi F5, 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:

  1. Trigger: Periodicità (ogni 1 ora), o pulsante manuale, o “Quando una riga viene modificata” se vuoi sincronizzazione guidata dai cambiamenti.
  2. Azione: Excel Online → Esegui script, puntalo alla tua cartella di lavoro e allo script qui sopra. Salva il valore di ritorno come contacts.
  3. 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
      }
  4. 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

BisognoMigliore opzione
Pulsante in un clic in una cartella di lavoro che il tuo team apre già ogni giornoMacro VBA (questa guida, metà superiore)
Cartella di lavoro in OneDrive/SharePoint, auto-sync orarioOffice Scripts + Power Automate (Premium necessario per HTTP)
Solo Mac Excel, non puoi usare VBAOffice Scripts + Power Automate
I dati vivono in Google Sheets, non ExcelApps 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 MBCSV 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.

Letture aggiuntive

Frequently Asked Questions

Una macro VBA può davvero chiamare l'API di Brevo da dentro Excel?
Sì. VBA può fare richieste HTTP tramite MSXML2.XMLHTTP, che è incluso in ogni installazione moderna di Windows. La macro fa POST di JSON a api.brevo.com/v3/contacts/import, lo stesso endpoint che colpirebbe uno script Python o Node. Mac Excel può farlo anche tramite Office Scripts + Power Automate, ma il VBA Mac desktop no (non c'è MSXML).
Posso eseguirla in modo programmato come con i trigger temporali di Apps Script?
Da VBA da solo no, Excel deve essere aperto perché Application.OnTime scatti. Per sincronizzazione programmata e non presidiata hai due opzioni: (1) Windows Task Scheduler che apre la cartella di lavoro ed esegue la macro, oppure (2) Office Scripts attivato da un flusso programmato di Power Automate (coperto più avanti in questa guida).
VBA è sicuro? La mia azienda blocca le macro.
Le macro VBA girano con accesso al filesystem e al registro, quindi bloccarle di default è una policy sensata. Due strade per aggirarla: (1) firmare la macro con un certificato di code signing e mettere la cartella di lavoro in una Trusted Location, oppure (2) saltare VBA del tutto e usare Office Scripts (TypeScript, in sandbox, senza accesso al filesystem). Office Scripts è la strada moderna benedetta da Microsoft.
Come si confronta con l'approccio Google Apps Script?
Apps Script è più vicino al set-and-forget, gira nel cloud di Google, trigger programmati integrati, niente licenza Excel o Power Automate. VBA vince per il lavoro offline e la sincronizzazione manuale in un clic da una cartella di lavoro che il tuo team ha già aperta. Office Scripts + Power Automate è l'equivalente cloud e programmato dal lato Microsoft, ma Power Automate spesso ha bisogno di un connettore premium a pagamento per l'HTTP in uscita.
Inizia gratis con Brevo