You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sendgrid API v3(Node.js)批量发送带动态模板数据的营销邮件求助

搞定SendGrid v3批量发送带动态模板数据的每周摘要!

嘿,我之前也踩过SendGrid这个坑,给你两个可行的方案,看哪个适配你的需求:

方案一:用Singlesends + 全局替换(不用手动捞联系人,最省心)

如果你的每周摘要内容是全局统一的(比如本周热门内容、通用公告,不是给每个用户的个性化内容),这个方案直接解决你的痛点,完全不用处理联系人列表:

  1. 先在模板里改变量格式
    把你动态模板里原来的{{dynamicTemplateData.title}}这类变量,改成{{-weekly_title-}}这种带短横线的格式(SendGrid Singlesends的全局替换变量要求用这种格式)。

  2. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:23:10