PAYATHON 2026

AWS SES v2 如何分页列出邮件或获取 MessageId?

数据阿明

SES v2 能分页列出所有邮件吗?

目前,SES v2 没有提供可通过 NextToken 分页枚举所有已发送邮件的 ListMessages API。GetMessageInsights 必须接收已知的 MessageId;CreateExportJob 可以按条件查找并导出邮件洞察数据,但不能作为完整邮件列表的分页接口。如果需要长期处理全部发送记录,建议在发送时保存 MessageId,再通过 event publishing 补充投递状态。SES v2 API 列表、GetMessageInsights 文档

这里所说的邮件记录包括 SES 的已发送邮件及其投递、互动记录。GetMessageInsights 返回地址、主题、标签和相关事件,但不提供原始邮件正文下载功能。

CreateExportJob 为什么不能替代分页?

MessageInsightsDataSource 使用 StartDate、EndDate、Include、Exclude 等条件筛选数据。MaxResults 只限制本次导出的结果数量,默认最多返回 1,000 条,允许的最大值是 10,000;它没有配套的下一页游标。MessageInsightsDataSource 文档

AWS 说明,筛选条件命中超过 10,000 条邮件时,API 返回的是其中的随机样本。因此,反复提交相同条件的导出任务,不能保证最终获得全部记录。VDM 的 Messages 查询范围为最近 30 天,也不能用来枚举无限期历史邮件。VDM 邮件查询与导出说明

需要补查历史数据时,可以按以下步骤处理:

  1. 确认所需时间范围内存在可查询的 Message Insights 数据。
  2. 调用 CreateExportJob,指定时间范围、筛选条件和导出格式。
  3. 保存返回的 JobId,通过 GetExportJob 定期检查状态,并处理失败或取消状态。
  4. 状态变为 COMPLETED 后,从 ExportDestination.S3Url 下载文件,提取邮件标识及所需字段。CreateExportJob 文档

如果结果达到数量上限,可以尝试缩小时间窗口或增加筛选条件。这是分段查询,不能视为服务端分页。StartDate 和 EndDate 都包含边界时间,因此相邻窗口可能产生重复记录。合并时应按数据粒度去重;保留收件人级记录时,不要只按 MessageId 去重。如果无法将单个窗口缩小到限制以内,仍不能保证完整获取。

后续邮件应该怎样保存 MessageId?

SendEmail 成功响应中包含 SES 分配的 MessageId。建议立即将它与业务记录、发送时间、收件人、AWS 账号和 Region 一起持久化。收到 MessageId 表示 SES 已接受邮件,不代表已经投递成功。SendEmail 文档

下面用 Python、Boto3 和 SQLite 演示如何在发送后保存 ID。运行前需要配置 AWS 凭证、可用的发送身份,以及满足当前账号发送限制的收件地址;mail-events 是需要提前创建的 configuration set。

import sqlite3
from datetime import datetime, timezone

import boto3

region = "us-east-1"
ses = boto3.client("sesv2", region_name=region)

with sqlite3.connect("ses_messages.db") as db:
    db.execute("""
        CREATE TABLE IF NOT EXISTS sent_messages (
            region TEXT NOT NULL,
            message_id TEXT NOT NULL,
            recipient TEXT NOT NULL,
            recorded_at TEXT NOT NULL,
            PRIMARY KEY (region, message_id)
        )
    """)

    recipient = "recipient@example.com"
    response = ses.send_email(
        FromEmailAddress="sender@example.com",
        Destination={"ToAddresses": [recipient]},
        Content={
            "Simple": {
                "Subject": {"Data": "Test email"},
                "Body": {"Text": {"Data": "Hello"}},
            }
        },
        ConfigurationSetName="mail-events",
    )

    db.execute(
        "INSERT INTO sent_messages VALUES (?, ?, ?, ?)",
        (
            region,
            response["MessageId"],
            recipient,
            datetime.now(timezone.utc).isoformat(),
        ),
    )

这是一个最小示例。生产环境还需要处理“SES 已接受邮件,但响应丢失或数据库写入失败”的情况。单靠发送后的数据库写入,无法保证记录不遗漏。可以在发送前建立业务发送任务,再通过事件记录核对和补齐。

建立自己的邮件索引后,就可以在数据库中分页遍历。只有需要额外洞察时,再调用 GetMessageInsights,以免逐封查询成为批处理瓶颈。该接口文档注明调用频率不超过每秒一次。GetMessageInsights 调用限制

event publishing 应该怎样配合使用?

建议同时使用发送记录和事件记录。发送记录保存业务关联和邮件 ID,事件记录补充发送、投递、退信、投诉等后续变化。

配置步骤如下:

  1. 创建 configuration set。
  2. 为它配置事件目标,例如 Amazon SNS,并启用需要的事件类型,至少考虑发送、投递、退信和投诉。
  3. 发送邮件时通过 ConfigurationSetName 关联该配置。
  4. 消费事件并保存到自己的数据库或存储系统。SES event publishing 配置说明

SNS 事件中的 mail.messageId 是 SES 分配的邮件 ID,可以与发送响应的 MessageId 关联。不要直接用原始邮件头中的 Message-ID 替代它。SES 事件字段说明

实现时建议保留原始事件,并处理重复消费和乱序到达。一个 MessageId 会对应多种事件,不能只按邮件 ID 去重,否则可能丢掉后续投递状态。如果需要保存邮件正文或附件,还应在发送链路中单独归档这些内容。

对于过去既没有保存发送响应、也没有留存事件的数据,只能在现有日志和可用的洞察查询范围内尝试补查,不能承诺恢复完整历史。后续启用事件发布,也不能补回此前未记录的事件。

备注:内容仅供参考。