Twilio SendGridのActivity feedとEmail Logsを比較し、CSVとJSONを確認する

Twilio SendGridのActivity feedとEmail Logsを比較し、CSVとJSONを確認する

Twilio SendGrid の Activity feed と Email Logs は、どちらも送信後の状況確認に使えますが、得意な作業が異なります。本記事では、Mail Send API でテストメールを送り、コンソールの CSV と API の JSON を確認する手順を紹介します。
2026.01.15

はじめに

本記事では、Twilio SendGrid の Email Activity Feed (以降 Activity feed) と Email Logs の用途差を説明します。あわせて、両機能を実際に操作し、CSV と JSON の出力を確認する手順を示します。

Twilio SendGrid とは

Twilio SendGrid は、メール送信を API として提供するサービスです。アプリケーションから HTTP リクエストでメール送信を行い、配送状況を追跡できます。

対象読者

  • Activity feed と Email Logs の違いを、運用設計や提案資料の根拠として把握したい方
  • 送信トラブル時に、コンソールと API の両方で状況を確認したい方
  • 取得したログを CSV や JSON として扱い、社内連携や二次利用を検討している方

参考

Activity feed と Email Logs について

Activity feed は、直近のメールイベントを一覧し、メッセージ単位の配送フローを追跡するための機能です。一覧は CSV でエクスポート可能です。保持期間のデフォルト日数はプランによって異なり、一般に 3 日または 7 日です。また Email Activity history add-on を購入すると、保持期間が最大 30 日になります。

一方で、 2025 年 11 月に新たにリリースされた Email Logs は、メールの配送経路や問題発生箇所を調べるための機能です。イベント単位のデータを JSON で取得でき、トラブルシュートや検証結果の保存に使えます。保持期間は 30 日で、延長はできません。

実際に使ってみた

Email Logs の UI フィルタは完全一致が前提であるため、検証では検索しやすいように件名を固定します。また、Email Logs API で取得する場合は API Key を用意します。

セットアップ

まず、作業用ディレクトリで依存関係を用意します。

npm init -y
npm i dotenv

.env を作成する

プロジェクト直下に .env を作成します。

SENDGRID_API_KEY=YOUR_SENDGRID_API_KEY_HERE

TO_EMAIL=your.address@example.com
FROM_EMAIL=verified.sender@example.com

SUBJECT=SG-LOGS-TEST-2026-01-15T1100+0900

Node.js スクリプトを作成する

sendgrid-tools.js を作成します。

sendgrid-tools.js 全文
/* sendgrid-tools.js */
const dotenv = require("dotenv");

dotenv.config();

function requireEnv(name) {
  const v = process.env[name];
  if (!v) {
    throw new Error(`${name} が未設定です。.env を確認してください。`);
  }
  return v;
}

function parseArgs(argv) {
  const args = { _: [] };
  for (let i = 0; i < argv.length; i += 1) {
    const a = argv[i];
    if (a.startsWith("--")) {
      const key = a.slice(2);
      const next = argv[i + 1];
      if (!next || next.startsWith("--")) {
        args[key] = true;
      } else {
        args[key] = next;
        i += 1;
      }
    } else {
      args._.push(a);
    }
  }
  return args;
}

async function sgFetch(url, { method, bodyObj } = {}) {
  const apiKey = requireEnv("SENDGRID_API_KEY");

  const res = await fetch(url, {
    method: method ?? "GET",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: bodyObj ? JSON.stringify(bodyObj) : undefined,
  });

  const text = await res.text();
  const xMessageId = res.headers.get("x-message-id");

  return {
    ok: res.ok,
    status: res.status,
    headers: {
      "x-message-id": xMessageId ?? "",
    },
    text,
    json: (() => {
      try {
        return text ? JSON.parse(text) : null;
      } catch {
        return null;
      }
    })(),
  };
}

function buildDefaultQuery() {
  const subject = requireEnv("SUBJECT");
  const toEmail = requireEnv("TO_EMAIL");
  return `subject = '${subject}' AND to_email = '${toEmail}'`;
}

async function cmdSend() {
  const toEmail = requireEnv("TO_EMAIL");
  const fromEmail = requireEnv("FROM_EMAIL");
  const subject = requireEnv("SUBJECT");

  const body = {
    personalizations: [
      {
        to: [{ email: toEmail }],
      },
    ],
    from: { email: fromEmail },
    subject,
    content: [{ type: "text/plain", value: "SendGrid logs test." }],
  };

  const r = await sgFetch("https://api.sendgrid.com/v3/mail/send", {
    method: "POST",
    bodyObj: body,
  });

  const out = {
    status: r.status,
    ok: r.ok,
    "x-message-id": r.headers["x-message-id"],
  };
  console.log(JSON.stringify(out, null, 2));

  if (!r.ok) {
    console.error(r.text);
    process.exitCode = 1;
  }
}

async function cmdSearch(args) {
  const limitRaw = args.limit ?? "10";
  const limit = Number(limitRaw);
  if (!Number.isFinite(limit) || limit < 1 || limit > 1000) {
    throw new Error("--limit は 1 から 1000 の範囲で指定してください。");
  }

  const query = args.query ? String(args.query) : buildDefaultQuery();

  const r = await sgFetch("https://api.sendgrid.com/v3/logs", {
    method: "POST",
    bodyObj: { query, limit },
  });

  const out = r.json ?? { status: r.status, ok: r.ok, raw: r.text };
  console.log(JSON.stringify(out, null, 2));

  if (!r.ok) {
    process.exitCode = 1;
  }
}

async function cmdDetail(args) {
  const sgMessageId = args._[0];
  if (!sgMessageId) {
    throw new Error("sg_message_id を指定してください。例: node sendgrid-tools.js detail <sg_message_id>");
  }

  const url = `https://api.sendgrid.com/v3/logs/${encodeURIComponent(sgMessageId)}`;
  const r = await sgFetch(url);

  const out = r.json ?? { status: r.status, ok: r.ok, raw: r.text };
  console.log(JSON.stringify(out, null, 2));

  if (!r.ok) {
    process.exitCode = 1;
  }
}

async function main() {
  const argv = process.argv.slice(2);
  const sub = argv[0];
  const args = parseArgs(argv.slice(1));

  try {
    if (sub === "send") {
      await cmdSend();
      return;
    }
    if (sub === "search") {
      await cmdSearch(args);
      return;
    }
    if (sub === "detail") {
      await cmdDetail(args);
      return;
    }

    console.log(
      [
        "使い方:",
        "  node sendgrid-tools.js send",
        "  node sendgrid-tools.js search --limit 10",
        "  node sendgrid-tools.js search --query \"subject = '...' AND to_email = '...'\"",
        "  node sendgrid-tools.js detail <sg_message_id>",
      ].join("\n"),
    );
    process.exitCode = 2;
  } catch (e) {
    console.error(e?.message ?? e);
    process.exitCode = 1;
  }
}

main();

Mail Send API でテストメールを送信する

node sendgrid-tools.js send

出力例:

{
  "status": 202,
  "ok": true,
  "x-message-id": "****"
}

Activity feed をコンソールで確認し CSV をダウンロードする

Activity feed はメッセージを選択すると、当該メッセージの詳細や履歴を確認できます。

details and history

次に、Export CSV を実行します。

Export CSV

ダウンロードした CSV の中身を確認します。カラム構成や値の粒度を見て、運用で使える情報量かどうかを判断します。

"processed","message_id","event","api_key_id","recv_message_id","credential_id","subject","from","email","asm_group_id","template_id","originating_ip","reason","outbound_ip","outbound_ip_type","mx","attempt","url","user_agent","type","is_unique","username","categories","marketing_campaign_id","marketing_campaign_name","marketing_campaign_split_id","marketing_campaign_version","unique_args"
"2026-01-15 04:47:02.000","****","processed","","****","","SG-LOGS-TEST-2026-01-15T1100+0900","****@example.com","****@example.com","","","***.***.***.***","","","","",0,"","","","","","[]","","","","","{}"
"2026-01-15 04:47:04.000","****","delivered","","****","","","","****@example.com","","","","250 2.0.0 OK DMARC:Quarantine **** - gsmtp","***.***.***.***","dedicated","gmail-smtp-in.l.google.com",0,"","","","","","[]","","","","","{}"

Email Logs をコンソールで検索する

Email Logs は、Recipient Email Address、Subject、Date、Category、Status などで検索できます。

Email logs

log details

Email Logs API で一覧を取得し、sg_message_id を得る

Email Logs の Filter all messages は POST /v3/logs です。query 文字列で条件を指定し、limit は 1 から 1000 の範囲です。

node sendgrid-tools.js search --limit 10

出力例 (実行結果のプレースホルダー):

{
  "messages": [
    {
      "from_email": "****@example.com",
      "sg_message_id": "****",
      "subject": "SG-LOGS-TEST-2026-01-15T1100+0900",
      "to_email": "****@example.com",
      "reason": "250 2.0.0 OK DMARC:Quarantine **** - gsmtp",
      "status": "delivered",
      "sg_message_id_created_at": "2026-01-15T04:47:02Z"
    }
  ]
}

必要なら、クエリを明示して実行できます。

node sendgrid-tools.js search --limit 10 --query "subject = 'SG-LOGS-TEST-...' AND to_email = '...'"

Email Logs API で 1 通のイベント詳細を取得する

Filter messages by ID は GET /v3/logs/{sg_message_id} です。Filter all messages で得た sg_message_id を指定すると、そのメッセージの full event timeline を取得できます。

node sendgrid-tools.js detail '先に取得した sg_message_id'

出力例 (実行結果のプレースホルダー):

{
  "from_email": "****@example.com",
  "sg_message_id": "****",
  "subject": "SG-LOGS-TEST-2026-01-15T1100+0900",
  "to_email": "****@example.com",
  "status": "delivered",
  "api_key_id": "****",
  "events": [
    {
      "event": "received",
      "recv_msgid": "****",
      "sg_event_id": "****",
      "timestamp": 1768452422,
      "api_key_id": "****",
      "api_version": "3",
      "client_ip": "***.***.***.***",
      "protocol": "HTTP",
      "recipient_count": 1,
      "reseller_id": "****",
      "size": 616,
      "useragent": "node",
      "v3_payload_details": {
        "customarg_count": 0,
        "substitution_bytes": 0,
        "substitution_count": 0,
        "content_bytes": 19,
        "recipient_count": 1,
        "sender_count": 1,
        "customarg_largest_bytes": 2,
        "text/plain": 1,
        "personalization_count": 1,
        "attachments_bytes": 0
      }
    },
    {
      "event": "processed",
      "email": "****@example.com",
      "sg_message_id": "****",
      "sg_event_id": "****",
      "timestamp": 1768452422,
      "smtp-id": "<****>"
    },
    {
      "event": "delivered",
      "email": "****@example.com",
      "sg_message_id": "****",
      "sg_event_id": "****",
      "timestamp": 1768452424,
      "smtp-id": "<****>",
      "ip": "***.***.***.***",
      "response": "250 2.0.0 OK DMARC:Quarantine **** - gsmtp",
      "tls": 1
    }
  ],
  "client_ip": "***.***.***.***",
  "outbound_ip": "***.***.***.***",
  "outbound_ip_type": "dedicated"
}

考察 - それぞれのユースケースについて

Email Logs は、特定メッセージの配送経路を追い、トラブルシュートする目的に向きます。UI で個別メッセージの詳細画面を確認でき、API でもイベント履歴を JSON として取得できます。「探し方が分かっている前提」の調査に強く 、たとえば 1/10 の 15:00 ごろに A さんへ送った件名 X のような形で、日付・宛先・メッセージ ID などの手がかりがあれば追いやすいです。

一方、Email Logs API はページング未対応であり、limit で件数を抑える前提です。このため、たとえば ログを漏れなく吸い上げて CSV 化し、AI に入力する といった用途では、期間の分割取得、重複排除、再試行、保存先の確保など、追加の設計・実装が必要になりやすいです。こうした用途では、コンソール操作で CSV をダウンロードし、関係者や AI へ共有できる Activity feed のほうが向いていると考えられます。

まとめ

Activity feed は、コンソールで網羅性の高い CSV をダウンロードし共有したい場面に向きます。Email Logs は、特定メッセージのトラブルシュートと、API によるイベント取得に向きます。ただし、大量データの長期保管や分析が必要な場合は、Event Webhook などの併用も考えるとよいでしょう。

この記事をシェアする

FacebookHatena blogX

関連記事