用 Apps Script 将 Google Sheets 联系人导入 Brevo

可重复运行的 Apps Script 示例:检查同意记录、映射字段、保留退订状态并显示 API 错误。

google apps script brevo

简短的回答是什么?

在表格中创建 email、firstName、lastName、consent 列。将 BREVO_API_KEY 放入 Script Properties,在代码中设置列表 ID,再运行 syncSheetToBrevo。脚本只处理已同意的行,检查 Brevo 的退订状态、去重,并在 API 错误时停止。这是需要自行维护的 Apps Script,不是内置连接器。

了解更多
在本页面

一次导出十二条活动报名记录很简单。第二次导入时,可能出现重复邮箱,也可能有人已在 Brevo 退订。可重复运行的导入必须处理这两种情况。

脚本使用 Brevo 联系人的读取、创建和更新端点。它面向小规模定时导入,不是批量加载器或 Google Sheets 内置连接器,也不会发送邮件。同意证据和脚本运行由你负责。

准备表格和列表

创建 Contacts 工作表,列标题必须如下。consent 使用复选框,只有获得对应营销用途的许可时才设为 TRUE。

emailfirstNamelastNameconsent
[email protected]JaneDoeTRUE
[email protected]AlexLeeFALSE

创建或选择 Brevo 列表,将其数字 ID 写入 LIST_ID。确认 Brevo 中已存在 FNAME 和 LNAME 属性;示例只映射这两个字段。如果表格无法证明何时及如何获得同意,请另行保存来源记录。单个 TRUE 单元格不是证据。

在 Extensions → Apps Script → Project Settings → Script Properties 中添加 BREVO_API_KEY。脚本属性对项目用户共享,应限制编辑权限。不要把密钥放在单元格或源码中。参见 Google 的属性访问规则。

脚本

用下例替换 Code.gs,首次运行前设置 LIST_ID。

const LIST_ID = 42; // Replace with your existing Brevo list ID.
const TAB = 'Contacts';
const BASE = 'https://api.brevo.com/v3';
function syncSheetToBrevo() {
const key = PropertiesService.getScriptProperties().getProperty('BREVO_API_KEY');
if (!key) throw new Error('Set BREVO_API_KEY in Script Properties first');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TAB);
if (!sheet) throw new Error('Contacts tab missing');
const values = sheet.getDataRange().getValues();
const headers = values.shift().map(h => String(h).trim());
const columns = ['email', 'firstName', 'lastName', 'consent']
.map(name => headers.indexOf(name));
if (columns.includes(-1)) throw new Error('Required header missing');
const [emailCol, firstCol, lastCol, consentCol] = columns;
const seen = new Set();
const result = { written: 0, skipped: 0 };
for (const row of values) {
const email = String(row[emailCol] || '').trim().toLowerCase();
if (row[consentCol] !== true || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || seen.has(email)) {
result.skipped++;
continue;
}
seen.add(email);
const path = '/contacts/' + encodeURIComponent(email);
const current = brevo_(key, path, 'get');
if (current.code !== 200 && current.code !== 404) {
throw new Error(`Contact read failed (${current.code}) for row ${result.written + result.skipped + 2}`);
}
if (current.code === 200) {
const contact = JSON.parse(current.text);
if (contact.emailBlacklisted || (contact.listUnsubscribed || []).includes(LIST_ID)) {
result.skipped++;
continue;
}
}
const attributes = {};
if (row[firstCol] !== '') attributes.FNAME = String(row[firstCol]);
if (row[lastCol] !== '') attributes.LNAME = String(row[lastCol]);
const body = { attributes, listIds: [LIST_ID] };
const saved = current.code === 404
? brevo_(key, '/contacts', 'post', { email, ...body })
: brevo_(key, path, 'put', body);
if (![201, 204].includes(saved.code)) {
throw new Error(`Contact write failed (${saved.code}) for ${email}: ${saved.text.slice(0, 200)}`);
}
result.written++;
}
Logger.log(JSON.stringify(result));
return result;
}
function brevo_(key, path, method, body) {
for (let attempt = 0; attempt < 3; attempt++) {
const response = UrlFetchApp.fetch(BASE + path, {
method,
contentType: 'application/json',
headers: { 'api-key': key },
payload: body ? JSON.stringify(body) : undefined,
muteHttpExceptions: true,
});
const code = response.getResponseCode();
if (code !== 429) return { code, text: response.getContentText() };
if (attempt === 2) return { code, text: response.getContentText() };
const reset = Object.entries(response.getHeaders()).find(
([name]) => name.toLowerCase() === 'x-sib-ratelimit-reset'
);
const raw = Number(reset && reset[1]);
const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt;
const waitSeconds = seconds + 2 ** attempt;
if (waitSeconds > 60) return { code, text: response.getContentText() };
Utilities.sleep(waitSeconds * 1000);
}
}

保存,选择 syncSheetToBrevo,点击 Run。Google 会请求 Sheets 和外部请求权限。在 Executions 查看 {written, skipped} 日志。读取返回 404 会创建联系人,返回 200 会更新;其他错误会停止。联系人写入成功不代表营销邮件已发送。

脚本在一次运行中去除重复邮箱,下次运行还会重新查询 Brevo。它不会把 emailBlacklisted 设为 false,也不会修改邮箱。如果 Brevo 报告全局屏蔽或已退出目标列表,即使表格仍是 TRUE,该行也会跳过。不要用它撤销退订。Brevo 提醒,修改被屏蔽联系人的邮箱可能解除屏蔽。

定时运行前检查一次

使用你控制的测试列表和联系人。一行 TRUE、一行 FALSE 加一个重复邮箱,应得到一次写入和两次跳过。让测试联系人在 Brevo 退订后重跑,该联系人不应再写入。测试错误时暂时设置无效的 LIST_ID,在 Executions 确认 HTTP 错误,再恢复 ID 并重试。不要用真实客户数据测试。本文代码已用模拟 Brevo 响应在本地验证,没有真实写入。

在 Apps Script → Triggers → Add Trigger 中选择 syncSheetToBrevo 和时间间隔。触发器以创建者账户运行,该账户须持续有表格权限。小表格可从每天一次开始,持续查看失败记录。Google 限制运行时间和 URL Fetch 使用量;大型数据源应拆成有界任务。

遇到 429,脚本读取 x-sib-ratelimit-reset,最多再试两次。如果需要等待超过 60 秒,就返回 429,留待下次运行,而不会提前重试。其他错误也会停止,下次运行从头开始。联系人可能在读取与写入之间退订;只有能接受这段短暂竞态时才适用。更严格的保护需要事件核对、可靠的同意记录,以及发送前的最后检查。

大表格可使用 Brevo 异步导入端点,它返回 processId,表示任务已接收,而不是已完成。报告成功前应检查结果。本文逐联系人处理可以定位失败的行。

常见问题

需要原生 Google Sheets 连接器吗?

不需要。示例在绑定表格的 Apps Script 项目中运行,并调用 Brevo 联系人 API。脚本、API 密钥和定时触发器都由你维护。

哪些行会发送给 Brevo?

只有 consent 为 TRUE 且邮箱有效的行。每次运行都会去重,写入前还会查询 Brevo。

重复运行会重新订阅已退订者吗?

不会。脚本跳过 Brevo 标记为 emailBlacklisted 或已退出目标列表的联系人,不会发送 emailBlacklisted: false,也不会改邮箱。上线前还应检查其他同意与屏蔽来源。

运行失败后如何重试?

查看 Apps Script 执行错误,修复原因后重跑同一函数。写入使用相同邮箱和列表 ID,已成功的行不会创建第二个联系人。

申请抢先体验

请输入您的名字和公司电子邮箱地址。我们会发送确认 Tajo 访问申请的链接。

获取Brevo