Amazon SES のメール送信方法や API バージョンなどを整理してみた

2023.05.21

いわさです。

サービスやアプリケーションでメール送信機能を考えるときに、Amazon SES が使えるかを検討することがあると思います。
そして、色々検討された結果 Amazon SES を使い始めたとします。
しかしいざ送信しようとすると複数の送信オプションがあることに気が付きます。

  • SMTP ではなく Amazon SES API から送信出来る
  • テンプレートを使ったり、raw データで送信したり出来る
  • 単発で送信したり、一括で送信したり出来る
  • そもそも SES の API に Version 1 と Version 2 が存在している

どういう時にどの方法で送信すれば良いのか、少しまとめたので紹介します。
既に以下の記事で Amazon SES 全般どのように利用したら良いのかについて説明されていますが、本記事では送信方法についてフォーカスしています。

先にまとめ

  • 新規でメール送信部分をこれから作るなら SMTP ではなく SES API で、かつ v2 を使う方が良い
    • SES API V2 での送信時のみ恩恵を受けれるアップデートが既にいくつか登場している。新規で v1 使うメリットはなさそう。
    • アプリが SMTP 前提だったり VPC エンドポイント経由で SES を使う場合は SMTP 使って良い
      • SES API に移行出来るとネットワークエラーになる可能性が下がり、スループットが向上する場合がある
    • ただ、既存のアプリケーションが SMTP が前提であったり、すでに SES API v1 が組み込まれている場合は現時点ではすぐに SES API v2 に移行すべきという感じではない。メールサイズやスループットの問題が出た場合に移行を検討するくらいの温度感
  • まずは raw 以外を要件にあわせて選択し、標準の送信機能で不足している場合は raw を検討する(添付ファイルとかカスタムヘッダーとか)
    • テンプレート使えるならどんどん使って大丈夫。サイズなどのテンプレート特有の制限事項には注意する
    • 単発で送信したいメールを無理に一括送信にまとめても SES 利用料金は安くはならない、API のコール数は減る

SMTP と SES API

Amazon SES は他の AWS サービスの API と同様に SES API エンドポイントが公開されていて、それを使ってメール送信を行うことが出来ます。
別の方法として SMTP を使って Amazon SES 経由でメール送信するオプションも提供されています。

私がこれまで Amazon SES を導入した際の半分以上は、システムの移行の際に既存で独自ホスティングしていた SMTP サーバーどうする?という問題への解決策として Amazon SES を導入するというものでした。
そのため、SMTP であることが大前提である場合はこの SMTP の機能を使うことになります。

ただし、これから新規でメール送信部分を構築するという場合は SMTP ではなく SES API が利用出来るかまず検討されるのが良いと思います。

シークレットの管理が必要

SMTP の場合は IAM 認証情報と似たようなアクセスキーとシークレットを発行し、それを SMTP のユーザー名とパスワードとして使用するのでシークレットの管理がどうしても必要になってしまいます。
一方で SES API の場合はインスタンスプロファイルや実行ロールなどがポリシーで Amazon SES の送信アクションが許可されているだけで良いです。
このあたりは IAM ユーザーのアクセスキーを発行する場合と似たようなリスクが発生します。

Amazon SES のスループットを上げるために SMTP エンドポイントではなく Amazon SES クエリ API の使用を推奨

さらに、公式ドキュメント上では SMTP 利用時にスループットを上げるために SMTP から SES API への移行を推奨していたりする記述があります。

Amazon SES SMTP エンドポイントとのやり取りには複数のネットワークリクエスト (たとえば、EHLO、MAIL FROM、RCPT TO、DATA、QUIT) から構成される SMTP 対話が含まれるのに対し、 クエリ API を使用すると、単一のネットワーク呼び出しを使用して E メール送信リクエストを送信できます。

参考:Amazon SES のスループットを上げる - Amazon Simple Email Service

ネットワークエラーの可能性が高くなる

上記観点はあまり知らなかったですが、類似の理由から SMTP を使っていて問題が生じやすくなる可能性についても別ページで記載されています。

SMTP は冗長プロトコルで、このプロトコルを使用して E メールを送信する場合は複数のネットワークラウンドトリップが必要になります。SMTP の性質上、ネットワークエラーの可能性は高くなります。

参考:Amazon SES SMTP の問題 - Amazon Simple Email Service

ただし VPC エンドポイントを使う場合は SMTP のみ

冒頭の記事でも言及されていますが、Amazon SES の VPC エンドポイントは SMTP のみサポートされています。
外部へのアウトバウンド通信が許可されていない環境で VPC エンドポイントが必要な場合は SMTP が前提となります。

API の v1 と v2

本日時点で Amazon SES の API には v1 と v2 の 2 つのバージョンが存在しています。

それぞれのバージョンにはリソースの互換があって、v1 で作成したリソースも v2 で作成したリソースも同じものとして作成されています。

ただし、一部の管理機能は v2 でのみサポートされているものがあります。
例えばアカウントレベルのサプレッションリストのインポートや一括削除は v2 の API でのみサポートされています。
ただし、サプレッションリスト自体は v1 でメール送信を行った場合でも考慮されます。

そのため、少し前までは v1 と v2 どちらでも良いですよ、新しい v2 を使っておきますか、という温度感の方が多かったのではないでしょうか。

しかし、1 年前の次のアップデートで v1 と v2 は大きく差がついたような気がします。

v1 は送受信ともに最大 10 MB までですが、v2 は送受信ともに最大 40 MB まで利用出来るようになりました。
他にもコンタクトリストや Pinpoint との統合などいくつかの機能は v2 でのみ利用が可能です。

こういった形で今後も性能向上のアップデートは v2 だけ恩恵を受けれる可能性が高いので新規で SES API を実装する場合は v2 を利用したほうが良いのではと思います。
ちなみに、SMTP 経由やマネジメントコンソールからメール配信をした場合は v2 が使われています。

送信コマンド

SES API の場合は次のように複数のコマンドが用意されています。
一見すると v1 と v2 で全然コマンドちゃうやんってなるのですが、コマンドの利用方法などよく見ると同じことが出来るということがわかります。

バージョン コマンド 内容
v1 send-email シンプル
v1 send-raw-email raw
v1 send-templated-email テンプレート・個別
v1 send-bulk-templated-email テンプレート・一括
v2 send-email 個別(シンプル・raw・テンプレート)
v2 send-bulk-email 一括・テンプレート

v1 では 4 つの送信コマンドが用意されていましたが、v2 では 2 つのコマンドにまとめられました。

シンプル、raw、テンプレート

ここでいう「シンプル」というのは、送信するメール本文や件名を API に全て引き渡すものです。
一方で「テンプレート」は事前にプレースホルダーを埋め込んだテンプレートを定義しておき、API に対しては対象テンプレートと埋め込み用のデータのみを渡すというものです。
上記 2 つの場合は宛先や本文など必要な情報をいくつか引き渡す形になります。その後 SES によって自動でメールヘッダーが生成されたり、あるいは場合によって本文が加工されたりします。(HTML メールの場合にトラッキング用の Web ビーコンが埋め込まれていたりします)

対して「raw」はヘッダーや本文を含む base64 エンコードされた raw データを直接 API に渡します。
これによって、通常の送信 API でサポートされていない添付ファイルを含めたり、メールヘッダーを独自に加工したりすることが出来ます。

この 3 つは、要件次第で選択する形になります。
ここでは v1 のテンプレートと raw の場合の送信サンプルを用意しました。raw はカスタマイズ性が高いですがちょっと実装が面倒だなという印象です。

まずはシンプルやテンプレートを使ってわかりやすい形の実装で十分か検討し、標準の送信機能で不足していて独自にカスタマイズが必要であれば raw を使うことになると思います。
SES API では簡単に送信することも出来るし、カスタマイズ性もあるよということはまず知っておくと良いですね。

テンプレートの作成と利用

% cat create-template.json                                            
{
    "Template": {
        "TemplateName": "hoge0520template2",
        "SubjectPart": "{{hoge}}: subject",
        "TextPart": "{{fuga}}: text",
        "HtmlPart": "<b>{{fuga}}: html</b>"
    }
}
% aws ses create-template --cli-input-json file://create-template.json
% cat send-templated-email.json
{
    "Source": "hoge0520@mail1.tak1wa.com",
    "Destination": {
        "ToAddresses": [
            "iwasa.takahito+ses@example.com"
        ]
    },
    "Template": "hoge0520template2",
    "TemplateData": "{\"hoge\":\"hoge3\",\"fuga\":\"fuga3\"}"
}
% aws ses send-templated-email --cli-input-json file://send-templated-email.json
{
    "MessageId": "01060188363c1301-a4a4c225-a7a7-40f5-8d3b-7a4a5ccd890c-000000"
}

なお、テンプレートの場合はテンプレート独自の制限事項(サイズとか)が存在していますので事前に確認しておきましょう。

raw メールの作成と送信

Delivered-To: iwasa.takahito+ses2@example.com
From: hoge0520+2@mail1.tak1wa.com
To: iwasa.takahito+ses2@example.com
Subject: hoge2 subject
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="----=_Part_3525986_821776089.1684534291572"

------=_Part_3525986_821776089.1684534291572
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit

hoge2 text
------=_Part_3525986_821776089.1684534291572
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit

hoge2 html

------=_Part_3525986_821776089.1684534291572--

上記を base64 エンコードしたものを raw データとして API に設定します。

% cat send-raw-email.json                                           
{
    "RawMessage": {
        "Data": "RGVsaXZlcmVkLVRvOiBpd2FzYS50YWthaGl0bytzZXMyQGNsYXNzbWV0aG9kLmpwDQpGcm9tOiBob2dlMDUyMCsyQG1haWwxLnRhazF3YS5jb20NClRvOiBpd2FzYS50YWthaGl0bytzZXMyQGNsYXNzbWV0aG9kLmpwDQpTdWJqZWN0OiBob2dlMiBzdWJqZWN0DQpNSU1FLVZlcnNpb246IDEuMA0KQ29udGVudC1UeXBlOiBtdWx0aXBhCCCvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PSItLS0tPV9QYXJ0XzM1MjU5ODZfODIxNzc2MDg5AAA2ODQ1MzQyOTE1NzIiDQoNCi0tLS0tLT1fUGFydF8zNTI1OTg2XzgyMTc3NjA4OS4xNjg0NTM0MjkxNTcyDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9VVRGLTgNCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IDdiaXQNCg0KBBBnZTIgdGV4dA0KLS0tLS0tPV9QYXJ0XzM1MjU5ODZfODIxNzc2MDg5LjE2ODQ1MzQyOTE1NzINCkNvbnRlbnQtVHlwZTogdGV4dC9odG1sOyBjaGFyc2V0PVVURi04DQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0DQoNCmhvZ2UyIGh0bWwNCg0KLS0tLS0tPV9QYXJ0XzM1MjU5ODZfODIxNzc2MDg5LjE2ODQ1MzQyOTE1NzItLQ=="
    }
}
% aws ses send-raw-email --cli-input-json file://send-raw-email.json
{
    "MessageId": "01060188361f50f2-e8ed615d-4f62-4cb7-ad7a-2989f061cb7d-000000"
}

個別と一括

個別は特定の宛先に送信するもので、一括は宛先リストと宛先ごとの埋め込みデータを渡すことで一度の API 呼び出しで複数の宛先(最大 50 件)にメール送信することが出来ます。
なお、個別の場合でも To や Cc、Bcc に複数指定することは可能です。

個別送信時に API の実行回数が増えることによるオーバーヘッドは気にする必要はあると思います、ただし以下の理由から個別で送るべきものをキューに入れて無理に一括送信するメリットは少ないと考えています。
シンプルにメールの送信タイミングが同じであるマーケティングメールのようなものあれば一括を使えば良いし、バラバラのトランザクションメールであれば個別を使えば良いと思います。

一括を指定した場合でも SES の利用料金は安くならない

Amazon SES の利用料金は以下です。

ここでは API 呼び出し回数は関係なくて、送信した E メールの件数による従量課金となっています。
そのため個別送信 API を 10 回呼び出した場合と、10 の宛先を指定した一括送信 API を 1 回呼び出した場合の料金は同じです。

送信 API の呼び出しレートは制限されていない

AWS API を実行する場合は API の実行レートを気にすると思います。
実は Amazon SES の API は 1 秒あたり 1 回までという制限があります。

そうなると、1 秒間に 1 通しかメールが送信出来ないのか、それなら一括であれば 50 通までは送れるのでそちらのほうが良いのでは、となりそうなのですが、上記の API レートは Amazon SES のメール送信 API に関しては対象外となっています。
アカウントごとにサービスクォータで送信件数ベースでの上限が設定されていますので API 実行レートではなくメールの送信レートを気にする必要があります。

さいごに

本日は Amazon SES の送信方法を整理してみました。
それぞれのまとめについては冒頭お伝えしました。

今回まとめていて改めて感じたのは、Amazon SES ではアプリケーションの互換性や送信要件によって多様な選択肢があります。
Web 上の記事では Amazon SES と他のメール送信サービスを比較した記事が多く存在しています。古い記事だと Amazon SES 厳しい...みたいな情報をたまに見ます。
実際のところは最新の Amazon SES では、レピュテーション周りやメール送信制限など以前よりもバージョンアップされていて、以前は Amazon SES だとちょっと厳しかったかもというケースでも最新では全然いけたりすることも多いです。
Amazon SES に興味を持った人は一度公式ドキュメントなどでも対応状況を確認してみてください。