วิธีส่งรายชื่อจาก Excel ไปยัง Brevo ด้วย VBA Macro (และทางเลือก Office Scripts)
VBA macro ที่ใช้งานได้จริงสำหรับโพสต์รายชื่อจากชีต Excel ไปยัง API ของ Brevo ในคลิกเดียว พร้อมแนวทางใช้ Office Scripts + Power Automate และการเปรียบเทียบกับการตั้งค่า Google Apps Script
คุณมีรายชื่อใน Excel และอยากให้อยู่ใน Brevo คำตอบเร็วที่สุดคือ Save As เป็น .csv แล้ว import ซึ่งใช้ได้ครั้งเดียว สำหรับงานที่ทำซ้ำ (การส่งต่อจากฝ่ายขายรายสัปดาห์ เวิร์กบุ๊กที่ทีมอัปเดตทุกวัน รายการพาร์ตเนอร์ที่อัปเดตเรื่อยๆ) คุณจะอยากได้ปุ่มใน Excel ที่ทำการซิงค์ให้
คู่มือนี้ครอบคลุมสองทางที่สมเหตุสมผลจริงๆ:
- VBA macro ฝังในเวิร์กบุ๊ก ไม่มีค่า license ไม่ใช้ cloud ทำงาน offline ได้ รันทันทีเมื่อผู้ใช้กดปุ่ม คำตอบที่ใช่สำหรับเคส Excel ไป Brevo ราว 80%
- Office Scripts + Power Automate ใช้ TypeScript แทน VBA รันใน cloud รองรับ trigger ตามตารางเวลา คำตอบที่ใช่เมื่อเวิร์กบุ๊กอยู่ใน OneDrive/SharePoint และต้องการซิงค์อัตโนมัติโดยไม่ต้องดูแล แต่ต้องระวังเรื่อง license Power Automate
ถ้าต้องการเวอร์ชันสำหรับ Google Sheets ดูได้ที่ บทความคู่ขนานเรื่อง Apps Script และถ้าแค่ต้องการ import CSV ครั้งเดียวจากสคริปต์บนแล็ปท็อป คู่มือ import CSV มีเวอร์ชัน Python, Node.js และ cURL
macro ทำอะไร
เมื่อผู้ใช้กดปุ่ม “Sync to Brevo” บนชีต:
- อ่านทุกแถวจากชีตที่กำลังใช้งาน (แถว header ก่อน หนึ่งรายชื่อต่อแถว)
- สร้าง JSON array ในรูปแบบที่ Brevo ต้องการสำหรับพารามิเตอร์
jsonBody - POST ไปที่
https://api.brevo.com/v3/contacts/importด้วย API key ที่เก็บไว้ในเวิร์กบุ๊ก - แสดงผลใน message box
แค่นั้น VBA ประมาณ 120 บรรทัด ด้านล่างคือโมดูลที่ทำงานได้สมบูรณ์
โครงชีตที่ macro คาดหวัง
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email เป็นฟิลด์บังคับ คอลัมน์อื่นทุกคอลัมน์จะกลายเป็น contact attribute ของ Brevo โดยแมปจาก header ของคอลัมน์ (ตัวพิมพ์ใหญ่) ไปเป็นชื่อ attribute ดังนั้น firstName → FIRSTNAME, company → COMPANY Custom attribute (ที่ไม่ใช่ชุดมาตรฐาน) ต้องมีในบัญชี Brevo ก่อน นิยามได้ที่ Contacts → Settings → Contact attributes
ขั้นที่ 1: เปิด VBA editor
ใน Excel กด Alt + F11 VBA editor จะเปิดขึ้น ที่ panel Project ทางซ้าย คลิกขวาที่เวิร์กบุ๊กแล้วเลือก Insert → Module จะมี Module1 เปล่าเกิดขึ้น
ขั้นที่ 2: วาง macro ทั้งก้อน
แทนที่เนื้อหาของ 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: Save เวิร์กบุ๊กเป็น .xlsm
VBA macro จะอยู่ในเวิร์กบุ๊กแบบเปิด macro ได้เท่านั้น Save As → เลือก Excel Macro-Enabled Workbook (.xlsm) รูปแบบ .xlsx ปกติจะตัด macro ออกแบบเงียบๆ หลายคนเสียโค้ดด้วยวิธีนี้ในครั้งแรก
ขั้นที่ 4: ตั้งค่า API key
รัน ConfigureApiKey หนึ่งครั้ง ทางเลือก:
- ใน VBA editor คลิกในตัวฟังก์ชัน
ConfigureApiKeyแล้วกดF5 - ใน Excel ไป Developer → Macros เลือก
ConfigureApiKeyแล้วกด Run
วาง key xkeysib-... ของคุณ macro จะเก็บเป็น custom document property ในตัวเวิร์กบุ๊กเอง ไม่อยู่ใน source code ไม่อยู่ใน registry และเดินทางไปกับไฟล์ (ระวัง: ถ้าส่ง .xlsm ทางอีเมลให้ใคร API key ก็จะติดไปด้วย)
ถ้าอยากเก็บ key ไว้นอกเวิร์กบุ๊ก เปลี่ยนที่เก็บไปที่ Windows registry:
' 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 ระดับผู้ใช้ ไม่ใช่ระดับเวิร์กบุ๊ก ใช้แบบนี้ถ้าหลายเวิร์กบุ๊กใช้ key เดียวกัน หรือไม่อยากให้ key อยู่ในไฟล์
ขั้นที่ 5: เพิ่มปุ่มบนชีต
ขั้นนี้คือสิ่งที่เปลี่ยนให้กลายเป็นประสบการณ์คลิกเดียวสำหรับผู้ใช้ที่ไม่ใช่สาย tech
Insert → Shapes → Rectangle วางลงบนชีต ตั้งชื่อว่า “Sync to Brevo” คลิกขวาที่ shape → Assign Macro → เลือก SyncSheetToBrevo เสร็จ
หรือถ้าต้องการ UI ที่เนี้ยบขึ้น เพิ่ม custom ribbon tab ผ่าน Office Custom UI Editor แต่สำหรับเครื่องมือใช้ภายในส่วนใหญ่ shape-as-button ก็เพียงพอ
ขั้นที่ 6: รัน
กดปุ่ม macro จะอ่านแถว แบ่ง batch โพสต์แต่ละ batch ไปที่ Brevo แล้วแสดง message box สรุป Brevo import เป็น asynchronous ดังนั้นข้อความสำเร็จหมายถึง Brevo รับ batch แล้ว การสร้าง contact จริงเกิดฝั่ง server ในไม่กี่วินาทีถัดมา Brevo จะส่งสรุปทางอีเมลเมื่อเสร็จ (ยกเว้นตั้ง disableNotification: true)
ปัญหาที่พบบ่อย
ปุ่มไม่ตอบสนองและไม่มีข้อผิดพลาด macro ถูกปิดใช้งาน ดูแถบความปลอดภัยสีเหลืองที่ด้านบนของชีตแล้วคลิก Enable Content ถ้าองค์กรบล็อก macro ดูแนวทาง trust-center / code-signing ด้านล่าง
Compile error: User-defined type not defined คุณกำลังใช้ Mac Excel ซึ่งไม่มี MSXML2.XMLHTTP Mac VBA ทำ HTTPS request ตรงๆ ไม่ได้ ใช้แนวทาง Office Scripts ด้านล่างแทน
400 Bad Request จาก Brevo โดยไม่มีสาเหตุชัดเจน เกือบทุกครั้งคือ (a) custom attribute ในชีตยังไม่มีใน Brevo สร้างก่อน หรือ (b) บั๊กเรื่อง JSON escape เครื่องหมายคำพูดหรือ backslash ในเซลล์ที่ไม่ได้ escape ฟังก์ชัน EscapeJson ในโค้ดจัดการเคสมาตรฐานให้ ถ้าข้อมูลมีอักขระแปลกๆ ให้ log payload ลง Immediate window (Debug.Print payload) แล้วตรวจสอบ
401 Unauthorized header ผิด ต้องเป็น api-key (ตัวพิมพ์เล็ก มีขีด) ไม่ใช่ Authorization macro ใช้ตัวที่ถูก แต่ถ้าก็อปสนิปเปตจากที่อื่นมา ตรวจอีกที
Excel ค้างเมื่อ import จำนวนมาก macro รันแบบ synchronous บน UI thread สำหรับ 50,000+ แถว Excel จะค้าง 10-30 วินาทีระหว่างสร้าง JSON และรอ Brevo จะยอมรับก็ได้ หรือเปลี่ยน MSXML2.XMLHTTP ไปใช้รูปแบบ async แต่ที่ scale ขนาดนี้ใช้ Power Automate ดีกว่า (หัวข้อถัดไป)
เมื่อ VBA ไม่พอ: Office Scripts + Power Automate
VBA ทำซิงค์ใน cloud ตามตารางเวลาไม่ได้ ถ้าต้องการ:
- เวิร์กบุ๊กซิงค์ไป Brevo ทุกชั่วโมงโดยไม่มีใครเปิด
- เวิร์กบุ๊กอยู่ใน OneDrive/SharePoint แก้ไขผ่านเว็บ
- ฝ่าย IT ห้าม macro บนเดสก์ท็อป
…ก็ใช้ Office Scripts (Apps Script เวอร์ชัน cloud ของ Microsoft) ร่วมกับ Power Automate (ระบบจัดตารางและ HTTP ของ Microsoft)
แบ่งหน้าที่: Office Scripts อ่านชีตและคืนค่าข้อมูล contact ส่วน Power Automate รับข้อมูลและ POST ไปที่ Brevo เมื่อทริกเกอร์
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;}Power Automate flow:
- Trigger: Recurrence (ทุก 1 ชั่วโมง) หรือปุ่ม manual หรือ “When a row is modified” ถ้าต้องการซิงค์ตามการเปลี่ยนแปลง
- Action: Excel Online → Run script ชี้ไปที่เวิร์กบุ๊กและสคริปต์ข้างต้น เก็บค่าที่คืนเป็น
contacts - Action: HTTP (เป็น Premium connector ดูหมายเหตุเรื่อง license ด้านล่าง)
- 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 → ถ้า status code ไม่ใช่ 202 ให้ส่งแจ้งเตือน Teams/อีเมล
ความจริงเรื่อง license: action HTTP เป็น Premium connector ของ Power Automate แพลน Microsoft 365 Business Basic/Standard ได้เฉพาะ standard connector ไม่รวม Premium ทางออกที่ถูกที่สุดคือ add-on Power Automate Premium (ราว $15/user/เดือน ณ เวลาที่เขียน) หรือย้ายการเรียก HTTP ไปยัง Azure Function เล็กๆ ที่ standard flow เรียกได้ ถ้าใช้ E3/E5 ที่รวม Premium อยู่แล้ว ไม่มีปัญหา
นี่คือเหตุผลหลักที่เรื่องราวฝั่ง Apps Script สะอาดกว่า: UrlFetchApp ของ Apps Script ฟรีและไม่จำกัด ส่วนของ Microsoft ซ่อนการเรียก network ไว้หลังขั้น connector แบบเสียเงิน
VBA, Office Scripts หรือ Apps Script ใช้ตัวไหนดี
| ความต้องการ | ตัวเลือกที่ดีที่สุด |
|---|---|
| ปุ่มคลิกเดียวในเวิร์กบุ๊กที่ทีมเปิดทุกวันอยู่แล้ว | VBA macro (คู่มือนี้ ครึ่งบน) |
| เวิร์กบุ๊กอยู่ใน OneDrive/SharePoint ซิงค์อัตโนมัติทุกชั่วโมง | Office Scripts + Power Automate (ต้องการ Premium สำหรับ HTTP) |
| ใช้ Mac Excel เท่านั้น VBA ใช้ไม่ได้ | Office Scripts + Power Automate |
| ข้อมูลอยู่ใน Google Sheets ไม่ใช่ Excel | Apps Script (ฟรี, มี trigger ตามตารางเวลาในตัว) |
| import ครั้งเดียว ไม่ต้องทำซ้ำ | Save As → CSV แล้วใช้ สคริปต์ import CSV |
| Bulk import จากไฟล์ใหญ่กว่า 10 MB | CSV ด้วย fileUrl ดู คู่มือ CSV |
ทำไมวิธีนี้ดีกว่า Zapier และแพลตฟอร์ม no-code
สำหรับงาน Excel-to-Brevo ที่ทำซ้ำ เครื่องมืออัตโนมัติของบุคคลที่สาม (Zapier, Make, n8n) คิดเงินตาม task และเพิ่มบุคคลที่สามคั่นกลางระหว่างข้อมูลของคุณกับ Brevo แนวทาง VBA ไม่มีค่าใช้จ่ายต่อเนื่อง ไม่มี data flow ผ่านบุคคลที่สาม และอยู่ในไฟล์ เมื่อเวิร์กบุ๊กเคลื่อนย้าย integration ก็ตามไปด้วย Office Scripts + Power Automate คล้ายกัน แต่ Microsoft เป็นบุคคลที่สาม (ซึ่งอยู่ใน stack อยู่แล้วถ้าใช้ M365)
หัวใจของ endpoint POST /v3/contacts/import ของ Brevo คือคุณไม่ต้องการแพลตฟอร์มเชื่อม เครื่องมือที่คุณมีก็รู้วิธียิง HTTP request อยู่แล้ว