Sendgrid API v3(Node.js)批量发送带动态模板数据的营销邮件求助
嘿,我之前也踩过SendGrid这个坑,给你两个可行的方案,看哪个适配你的需求:
方案一:用Singlesends + 全局替换(不用手动捞联系人,最省心)
如果你的每周摘要内容是全局统一的(比如本周热门内容、通用公告,不是给每个用户的个性化内容),这个方案直接解决你的痛点,完全不用处理联系人列表:
先在模板里改变量格式
把你动态模板里原来的{{dynamicTemplateData.title}}这类变量,改成{{-weekly_title-}}这种带短横线的格式(SendGrid Singlesends的全局替换变量要求用这种格式)。修改Singlesend的配置代码
删掉没用的dynamicTemplateData,换成substitutions参数传你的动态内容:const sgClient = require('@sendgrid/client') sgClient.setApiKey(config.sendgrid_key) const params = { 'name': 'Weekly Digest #'+nb, 'sender_id': sg_sender_id, 'suppression_group_id': sg_unsub_group_id, 'template_id': id_template, // 这里传全局替换的键值对,对应模板里的{{-key-}} 'substitutions': { '-weekly_title-': template_params.title, '-weekly_content-': template_params.content, '-weekly_link-': template_params.link }, 'filter': {'send_to_all': true} } const url = '/v3/marketing/singlesends/' + campaign_id.toString('base64') const request = { method: 'PATCH', url, body: params } sgClient.request(request) .then(([response, body]) => console.log('Singlesend updated:', body)) .catch(error => console.error('Error:', error.toString()))这样Singlesend发送时,会自动把模板里的替换变量换成你传的内容,同时自动跳过已退订该分组的用户,完美契合你的需求!
方案二:批量发送(适合需要个性化内容的场景)
如果你的摘要需要给每个用户展示专属内容(比如用户的未读消息、个性化推荐),那得先拿到所有符合条件的联系人,再批量发:
第一步:正确捞取所有未退订的联系人
你之前说/v3/marketing/contacts只能拿50个,是因为没开分页!这个接口支持最多一次拿1000个,循环就能捞完所有用户,还能直接过滤掉已退订的:
async function getAllValidContacts(suppressionGroupId) { let allContacts = [] let page = 1 const pageSize = 1000 // SendGrid允许的最大单页数量 while (true) { const request = { method: 'GET', url: '/v3/marketing/contacts', qs: { page_size: pageSize, page: page, // 过滤条件:未退订指定分组的用户 query: `asm_groups_suppressed NOT CONTAINS ${suppressionGroupId}` } } const [response, body] = await sgClient.request(request) if (body.result.length === 0) break // 没数据了就停止循环 allContacts.push(...body.result) page++ } return allContacts }
第二步:用批量接口发送带动态数据的邮件
拿到联系人列表后,用@sendgrid/mail的批量发送功能,给每个用户指定动态模板数据,同时加上退订分组的双重保障:
const sgMail = require('@sendgrid/mail') sgMail.setApiKey(config.sendgrid_key) async function sendPersonalizedDigest(contacts, templateId, globalData) { // 把联系人分成每1000一组(SendGrid批量发送的上限) const messageChunks = [] for (let i = 0; i < contacts.length; i += 1000) { const chunk = contacts.slice(i, i + 1000).map(contact => ({ to: contact.email, from: { email: 'your-sender@example.com', name: 'Weekly Digest' }, templateId: templateId, // 合并全局数据和用户个性化数据 dynamicTemplateData: { ...globalData, user_name: contact.first_name || 'Dear Friend' // 这里可以加用户专属的字段,比如contact.custom_fields.your_field }, // 自动跳过已退订该分组的用户 asm: { groupId: sg_unsub_group_id } })) messageChunks.push(chunk) } // 分批发送 for (const chunk of messageChunks) { await sgMail.send(chunk) console.log(`Sent ${chunk.length} personalized digests`) } }
最后调用流程
async function runWeeklyDigest() { try { const validContacts = await getAllValidContacts(sg_unsub_group_id) await sendPersonalizedDigest(validContacts, id_template, template_params) console.log('All weekly digests sent successfully!') } catch (err) { console.error('Failed to send digests:', err) } } runWeeklyDigest()
为啥你原来的代码没用?
Singlesends API根本不支持dynamicTemplateData这个参数!这个参数是给单封事务邮件用的,批量营销邮件的动态内容得用substitutions全局替换,或者自定义字段,这就是你之前传了也没效果的原因~
内容的提问来源于stack exchange,提问作者Marie Dm

