Contents
1. 24 時間ローリングクォータと秒単位レートの概要
このセクションでは、SES の送信制限がどのように計測されるかを説明し、変動する数値については最新版ドキュメントへの参照リンクと取得日を明示します。
参考:2024 年 10 月 01 日版「Amazon SES 送信制限の管理」
デイリークォータ(24 時間ローリング)
デイリークォータは過去 24 時間分の合計送信数でカウントされ、上限を超えると即座にエラーが返ります。
| 項目 | 説明 |
|---|---|
| デイリークォータ | 新規アカウントの場合は 200 通/日 が標準(コンソールで確認可)。上限は AWS の審査に応じて増加可能です。 |
| 最大送信レート | デフォルトでは 14 メール/秒 程度が設定されていますが、リージョンやアカウントの利用実績により変動します。 |
注:数値は上記リンクの「Sending Limits」ページで常に最新情報を確認してください。
秒単位レートの仕組み
SES は 1 秒あたりに許容できるメール送信数をトークンバケット方式で管理しています。
- トークン生成:秒ごとに上限分だけトークンが補充され、トークンが残っている間はリクエストが受理されます。
- トークン不足:レートを超えるリクエストは
ThrottlingException(日本語では「Maximum sending rate exceeded」)として即座に返却されます。
2. サンドボックス環境から本番環境への移行手順
新規アカウントはまず サンドボックス に配置され、メール送信先が認証済みアドレス/ドメインに限定されます。本節では、制限差異と解除フローを具体的に示します。
制限比較表(2024 年 10 月時点)
| 項目 | サンドボックス | 本番環境(デフォルト) |
|---|---|---|
| デイリークォータ | 200 通/日 | 10,000 通/日以上(増加申請可) |
| 最大送信レート | 1 メール/秒 | 14 メール/秒以上 |
| 宛先制限 | 認証済みメールアドレス/ドメインのみ | 任意の宛先へ送信可能 |
本番環境への移行フロー(コンソール操作)
以下の手順でサンドボックス解除とクォータ増加申請を同時に行います。
- SES コンソール → 「Sending Statistics」画面で現在のステータスを確認。
- 左メニューの「Sending Limits」→「Request a Sending Limit Increase」をクリック。
- 必要情報(利用ケース、予測送信量、過去実績)を入力し送信。審査が通れば自動で本番環境へ移行します。
ポイント:申請時に具体的なビジネスユースケースと CloudWatch メトリクスから抽出した実績データを添付すると承認率が向上します。
3. エラー別対策 – Maximum sending rate exceeded
秒単位レート超過は瞬間的な送信スパイクで発生しやすく、適切にレート制御しないと配信停止につながります。本節では原因の整理と指数バックオフ実装例を示します。
原因とスロットリングの基本メカニズム
トークンバケット方式により、1 秒あたり許容されたメール数以上がキューに入るとエラーが返ります。
- 典型的なシナリオ:Cron ジョブで 1 分間に 10,000 通送信 → 平均 166 通/秒 とレート上限を大幅に超過。
- 影響範囲:失敗したリクエストは再送しないとメールが欠落するため、バックオフロジックの実装が必須です。
指数バックオフ実装例(Bilingual コメント)
Node.js(AWS SDK for JavaScript v3)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses"; const client = new SESClient({ region: "us-east-1" }); const MAX_RETRIES = 5; /** * メール送信を指数バックオフでリトライする関数 * Sends an email with exponential backoff retries on throttling. */ async function sendWithBackoff(params) { let attempt = 0; while (attempt <= MAX_RETRIES) { try { await client.send(new SendEmailCommand(params)); console.log("メール送信成功"); // Success return; } catch (err) { if (err.name !== "ThrottlingException") throw err; // 非スロットリング例外は即時失敗 const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s... console.warn(`レート超過、${delay}ms 後にリトライ`); // Retry after backoff await new Promise(res => setTimeout(res, delay)); attempt++; } } throw new Error("最大リトライ回数を超えました"); // Exceeded max retries } |
Python(boto3)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import time import boto3 from botocore.exceptions import ClientError ses = boto3.client('ses', region_name='us-east-1') MAX_RETRIES = 5 def send_with_backoff(**kwargs): """ Sends an email with exponential backoff on ThrottlingException. スロットリング時に指数バックオフでリトライする。 """ attempt = 0 while attempt <= MAX_RETRIES: try: ses.send_email(**kwargs) print("メール送信成功") # Success return except ClientError as e: if e.response['Error']['Code'] != 'ThrottlingException': raise # 非スロットリングは再送しない delay = (2 ** attempt) # 秒単位のバックオフ (1, 2, 4…) print(f"レート超過、{delay}s 後にリトライ") time.sleep(delay) attempt += 1 raise RuntimeError("最大リトライ回数を超えました") # Exceeded max retries |
ベストプラクティス:バックオフ待機中は他のキューイングロジックと併用し、同時リクエスト数自体も制限するとさらに安定します。
4. エラー別対策 – Daily sending quota exceeded
デイリークォータ超過は 24 時間ローリング上限を越えた場合に発生し、大量バッチ処理で特に顕在化します。ここでは原因と削減戦略、そして実装例を紹介します。
原因と送信量削減の戦略
総送信数がクォータ上限に近づくと、途中からエラーが返り始めます。
| 戦略 | 具体的な手法 | 効果 |
|---|---|---|
| バッチ化 | SendBulkEmail API を活用し、1 回のリクエストで最大 50 件までまとめて送信 |
API 呼び出し回数削減・レート分散 |
| キューイング | Amazon SQS と Lambda の組み合わせで 1 秒あたりの処理件数を制御 | スロットリング回避・再試行管理 |
| 送信スケジュール調整 | ピーク時間帯を避け、夜間やトラフィックが少ない時間に配信 | クォータ余裕確保 |
バッチ化とキューイングの実装例
Node.js(SendBulkEmail)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import { SESClient, SendBulkEmailCommand } from "@aws-sdk/client-ses"; const client = new SESClient({ region: "us-east-1" }); /** * 複数宛先へ一括送信する関数 * Sends bulk email to many recipients in a single API call. */ async function sendBulk(emails) { const params = { DefaultContent: { Simple: { Subject: { Data: "ご案内" }, Body: { Text: { Data: "本文です。" } } } }, Destinations: emails.map(to => ({ Destination: { ToAddresses: [to] } })) }; await client.send(new SendBulkEmailCommand(params)); } |
Python(SQS + Lambda)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import boto3, json sqs = boto3.client('sqs') ses = boto3.client('ses') QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789012/email-queue" def enqueue_email(to_address): """ メール送信情報を SQS にキューイングする。 Enqueues email data to SQS for asynchronous processing. """ sqs.send_message( QueueUrl=QUEUE_URL, MessageBody=json.dumps({"to": to_address}) ) def lambda_handler(event, context): """SQS から取得したメッセージを SES に渡して送信する Lambda ハンドラ""" for record in event['Records']: msg = json.loads(record['body']) ses.send_email( Source="no-reply@example.com", Destination={"ToAddresses": [msg["to"]]}, Message={ "Subject": {"Data": "ご案内"}, "Body": {"Text": {"Data": "本文です。"}} } ) |
ポイント:キューイングは CloudWatch アラームと連携させ、クォータ逼迫時に自動でポーリングレートを下げることが可能です。
5. クォータ増加リクエスト手順と必要情報
ビジネス成長に伴い送信量が既存クォータを上回る場合は、AWS Service Quotas から増加申請します。以下のフローと添付資料例を参考にしてください。
手順(ステップバイステップ)
- AWS マネジメントコンソール → 「Service Quotas」へ遷移。
- 左メニューで Amazon Simple Email Service (SES) を選択。
- 増加希望項目(
Daily sending quota、Maximum send rate)の Request quota increase ボタンをクリック。 - フォームに以下を入力し送信:
- 利用ケース(例:月間 1,000,000 通のニュースレター配信)
- 過去 30 日間の送信実績(CloudWatch
Sendメトリクスから取得) - 将来予測とビジネスインパクトの説明
- 審査完了メールが届くまで待機(通常 1〜2 営業日)。
提出すべき情報テンプレート
| 項目 | 推奨記載内容 |
|---|---|
| ビジネス目的 | 「顧客向けプロモーション」「システム通知」など具体的に列挙 |
| 現在のクォータ使用率 | Daily sending quota: 8,500 / 10,000 (85%)(CloudWatch データ) |
| 過去実績 | 総送信数、ピーク時レート、失敗率(例:30 日で 260,000 通、最大 12 メール/秒、エラー率 0.2%) |
| 将来予測 | 「次四半期で 2 倍に増加予定」など根拠付き数値 |
| リスク評価 | クォータ不足がサービス停止につながる旨を簡潔に記載 |
コツ:数値は必ず公式メトリクスから取得し、主観的な推測だけでなく実績ベースの根拠を示すと承認率が上がります。
6. モニタリング・運用最適化
安定したメール配信には リアルタイムモニタリング と 接続管理 が欠かせません。ここでは CloudWatch アラーム設定、SMTP 接続プール活用、Dedicated IP の取得とウォームアップ手順を具体的に解説します。
6.1 CloudWatch メトリクスで送信レートと使用率を可視化
| メトリクス | 説明 |
|---|---|
Send |
1 分間あたりの送信数(レート把握に利用) |
Delivery |
配信成功メール数 |
Reject |
SES が受け付けなかったリクエスト数(レート超過等) |
Complaint |
受信者からの苦情件数 |
アラーム例:送信レートが上限の 80% を超えたら通知
|
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "AlarmName": "SES-HighSendRate", "MetricName": "Send", "Namespace": "AWS/SES", "Statistic": "Sum", "Period": 60, "EvaluationPeriods": 1, "Threshold": 1120, // 14 メール/秒 × 60 秒 × 0.8 "ComparisonOperator": "GreaterThanThreshold", "AlarmActions": ["arn:aws:sns:us-east-1:123456789012:SESAlerts"] } |
ポイント:アラームは SNS トピックに連携し、Lambda で自動的にレート制御パラメータを緩める仕組みと併用すると効果的です。
6.2 SMTP 接続プールと再利用による接続数制限対策
SES の SMTP エンドポイントは IP 当たり約 50 接続 が上限です。大量送信シナリオではプーリングでハンドシェイク回数を削減し、421 系エラーの発生を防ぎます。
Node.js(nodemailer)サンプル(日本語・英語コメント)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const nodemailer = require("nodemailer"); // Create a pooled transporter – プール接続で効率化 (Efficient pooled connection) const transporter = nodemailer.createTransport({ host: "email-smtp.us-east-1.amazonaws.com", port: 587, secure: false, auth: { user: process.env.SMTP_USER, // SMTP username pass: process.env.SMTP_PASS // SMTP password }, pool: true, // Enable connection pooling maxConnections: 30, // Keep below the service limit (30 < 50) rateLimit: 10 // Limit to 10 emails/second (adjust as needed) }); |
Python(smtplib + 手動プール)サンプル
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import smtplib from email.mime.text import MIMEText class SmtpPool: """Simple SMTP connection pool. シンプルなSMTP接続プール実装.""" def __init__(self, host, port, user, password, max_conn=20): self.host = host self.port = port self.user = user self.password = password self.pool = [self._new_connection() for _ in range(max_conn)] def _new_connection(self): conn = smtplib.SMTP(self.host, self.port) conn.starttls() conn.login(self.user, self.password) return conn def send(self, msg: MIMEText): conn = self.pool.pop() # Acquire a connection try: conn.send_message(msg) # Send the email finally: self.pool.append(conn) # Return it to the pool # Usage example pool = SmtpPool("email-smtp.us-east-1.amazonaws.com", 587, "SMTP_USER", "SMTP_PASS") msg = MIMEText("本文です。") msg["Subject"] = "件名" msg["From"] = "no-reply@example.com" msg["To"] = "user@example.com" pool.send(msg) |
ベストプラクティス:
maxConnectionsは上限の 70% 程度に抑え、rateLimit(Node)や自前のスロットリングロジックで秒間送信数も制御します。
6.3 Dedicated IP とウォームアップ手順
大量配信やブランドメールでは Dedicated IP を取得し、段階的に送信量を増やす「ウォームアップ」プロセスが推奨されます。
- IP の取得:SES コンソールの「Dedicated IP」から購入。
- 30 日間のウォームアップ例(※実績に合わせて調整可)
| Day | 送信量 (全体に対する %) |
|---|---|
| 1‑3 | 5% |
| 4‑7 | 10% |
| 8‑14 | 25% |
| 15‑21 | 50% |
| 22‑30 | 100% |
- モニタリング:毎日
ComplaintとBounceを CloudWatch で確認し、異常があれば即座に送信量を停止。
結論:Dedicated IP と計画的ウォームアップは送信ドメインの評判維持に直結し、長期的な配信成功率向上につながります。
7. SDK による送信レート自動調整
実運用では CloudWatch メトリクス を取得しリアルタイムでレートを評価、必要に応じてバックオフさせるロジックが有効です。以下は Node.js と Python のサンプルコードです(コメントは日本語・英語併記)。
7.1 Node.js(AWS SDK for JavaScript v3)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import { CloudWatchClient, GetMetricStatisticsCommand } from "@aws-sdk/client-cloudwatch"; import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses"; const cw = new CloudWatchClient({ region: "us-east-1" }); const ses = new SESClient({ region: "us-east-1" }); /** * 現在の 1 秒平均送信レートを取得する * Retrieves the average send rate (emails per second) over the past 5 minutes. */ async function getCurrentSendRate() { const now = new Date(); const params = { Namespace: "AWS/SES", MetricName: "Send", StartTime: new Date(now - 5 * 60 * 1000), // 5 分前 EndTime: now, Period: 60, // 1 分間隔 Statistics: ["Sum"] }; const data = await cw.send(new GetMetricStatisticsCommand(params)); const sum = data.Datapoints?.[0]?.Sum ?? 0; return sum / 60; // 秒あたりの平均送信数 } /** * レートが上限に近い場合はバックオフしてメールを送信する * Performs exponential backoff if the current rate approaches the limit. */ async function sendWithDynamicRate(emailParams) { const rate = await getCurrentSendRate(); const MAX_ALLOWED = 14; // デフォルト上限 (adjust per account) if (rate >= MAX_ALLOWED * 0.9) { // 90% を超えたらスロットリング console.warn(`現在レート ${rate.toFixed(2)} が上限付近です。2 秒待機します`); await new Promise(r => setTimeout(r, 2000)); } return ses.send(new SendEmailCommand(emailParams)); } |
7.2 Python(boto3)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import time import boto3 cloudwatch = boto3.client('cloudwatch', region_name='us-east-1') ses = boto3.client('ses', region_name='us-east-1') def get_current_send_rate(): """ Retrieves the average send rate (emails per second) from CloudWatch. CloudWatch から過去5分間の送信合計を取得し、秒単位に換算する。 """ now = time.time() resp = cloudwatch.get_metric_statistics( Namespace='AWS/SES', MetricName='Send', StartTime=time.gmtime(now - 300), # 5 分前 EndTime=time.gmtime(now), Period=60, Statistics=['Sum'] ) datapoints = resp['Datapoints'] if not datapoints: return 0 max_sum = max(dp['Sum'] for dp in datapoints) return max_sum / 60 # 秒あたりの平均 def send_with_dynamic_rate(email_kwargs): """ Sends email with automatic backoff when the current rate approaches the quota. 現在レートが上限に近い場合は2秒待機してから送信する。 """ rate = get_current_send_rate() MAX_RATE = 14 if rate >= MAX_RATE * 0.9: print(f"レート {rate:.2f} が上限付近です。2 秒待機します") time.sleep(2) ses.send_email(**email_kwargs) # Example usage send_with_dynamic_rate({ 'Source': 'no-reply@example.com', 'Destination': {'ToAddresses': ['user@example.com']}, 'Message': { 'Subject': {'Data': 'テストメール'}, 'Body': {'Text': {'Data': '本文です。'}} } }) |
まとめ:CloudWatch でリアルタイムにレートを監視し、閾値付近で自動的にバックオフすることで
Maximum sending rate exceededエラーの発生率を大幅に低減できます。
全体まとめ
- クォータは変動するため、常に公式ドキュメント(取得日付き)を参照し、コンソールで実測値を確認しましょう。
- サンドボックス解除は利用ケースと実績データの提出が鍵です。具体的かつ定量的な情報を用意してください。
- レート超過対策は指数バックオフ+キューイングで実装し、コード内に日本語・英語コメントを併記して国際的な可読性を確保します。
- デイリークォータ対策はバッチ化・キューイング・送信スケジュール調整の3本柱で設計し、実装例を活用してください。
- 増加リクエストは CloudWatch メトリクスに基づく定量データと将来予測を添付すると承認率が向上します。
- 運用最適化は CloudWatch アラーム、SMTP 接続プール、Dedicated IP の取得・ウォームアップの3層防御で実現します。
これらのベストプラクティスを組み合わせることで、Amazon SES を安全かつ拡張性の高いメール送信基盤として活用できるようになります。