<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shreyash</title>
    <description>The latest articles on DEV Community by Shreyash (@shreyash_1b88a959).</description>
    <link>https://dev.to/shreyash_1b88a959</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3842040%2F29104022-8ed2-4561-8133-7557fa5df1e3.webp</url>
      <title>DEV Community: Shreyash</title>
      <link>https://dev.to/shreyash_1b88a959</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shreyash_1b88a959"/>
    <language>en</language>
    <item>
      <title>Amazon SES Review: The Brutal Truth for Next.js Developers (2026)</title>
      <dc:creator>Shreyash</dc:creator>
      <pubDate>Thu, 26 Mar 2026 15:44:29 +0000</pubDate>
      <link>https://dev.to/shreyash_1b88a959/amazon-ses-review-the-brutal-truth-for-nextjs-developers-2026-36g9</link>
      <guid>https://dev.to/shreyash_1b88a959/amazon-ses-review-the-brutal-truth-for-nextjs-developers-2026-36g9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📖 This is a condensed version. Read the &lt;strong&gt;&lt;a href="https://www.devdecide.com/reviews/amazon-ses-review" rel="noopener noreferrer"&gt;full Amazon SES review with code examples and setup guide&lt;/a&gt;&lt;/strong&gt; on DevDecide.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Most "best email provider" articles are written by SEO agencies chasing affiliate commissions. This one isn't.&lt;/p&gt;

&lt;p&gt;We integrated Amazon SES into a real Next.js app — configured DKIM/SPF/DMARC, sent transactional emails, handled bounces via SNS webhooks, and intentionally broke things to see what happens. Here's the honest breakdown.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Even Consider Amazon SES?
&lt;/h2&gt;

&lt;p&gt;The pricing speaks for itself:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;100,000 emails/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SendGrid&lt;/td&gt;
&lt;td&gt;~$89.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resend&lt;/td&gt;
&lt;td&gt;~$35.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon SES&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$10.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At scale (500k emails/month), SendGrid costs ~$249. SES? &lt;strong&gt;$50.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But AWS makes you earn every penny of those savings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Good: Code Experience is Excellent ✅
&lt;/h2&gt;

&lt;p&gt;The AWS SDK v3 is modular, lean, and async-native — perfect for Next.js Server Actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws-sdk/client-ses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SESClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SendEmailCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-ses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sesClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SESClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendWelcomeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendEmailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello@your-saas.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ToAddresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome aboard!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;We are thrilled to have you.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sesClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, promise-based, minimal boilerplate. Rating: &lt;strong&gt;4.5/5&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ugly: Sandbox + Bounce Handling ⚠️
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Sandbox Trap
&lt;/h3&gt;

&lt;p&gt;Every new SES account starts in a sandbox where you can only send to manually verified addresses (200 emails/day max). Getting production access requires submitting a detailed support ticket — and waiting &lt;strong&gt;24–72 hours&lt;/strong&gt;. Don't leave this for your launch night.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bounces Will Destroy Your Account
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bounce rate &amp;gt; 5% → account review&lt;/li&gt;
&lt;li&gt;Bounce rate &amp;gt; 10% → &lt;strong&gt;permanent ban&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SES doesn't track this automatically. You must wire up an &lt;strong&gt;SNS topic → webhook → database blacklist&lt;/strong&gt; pipeline yourself. This is non-trivial infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Use Amazon SES?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;✅ Use it if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are comfortable with DNS records (DKIM, SPF, DMARC)&lt;/li&gt;
&lt;li&gt;Can build and maintain bounce-handling webhooks&lt;/li&gt;
&lt;li&gt;Want to aggressively control infrastructure costs at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ Avoid it if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want plug-and-play email with zero DevOps&lt;/li&gt;
&lt;li&gt;Need a visual dashboard, analytics, or drag-and-drop templates&lt;/li&gt;
&lt;li&gt;Can't afford time for a multi-day setup process&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Our Final Rating
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Developer Experience&lt;/td&gt;
&lt;td&gt;4.5 / 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deliverability&lt;/td&gt;
&lt;td&gt;5 / 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;5 / 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup Complexity&lt;/td&gt;
&lt;td&gt;3 / 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overall&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.8 / 5&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Amazon SES is raw infrastructure at wholesale prices. If you're willing to do the engineering work, nothing beats it for transactional email at scale.&lt;/p&gt;

&lt;p&gt;👉 Get the &lt;strong&gt;&lt;a href="https://www.devdecide.com/reviews/amazon-ses-review" rel="noopener noreferrer"&gt;full guide with IAM setup, SNS bounce handling, and production-ready code&lt;/a&gt;&lt;/strong&gt; on DevDecide.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>Amazon SES Setup: The Things Nobody Tells You (Bounce Handling, DKIM, IAM)</title>
      <dc:creator>Shreyash</dc:creator>
      <pubDate>Wed, 25 Mar 2026 18:18:00 +0000</pubDate>
      <link>https://dev.to/shreyash_1b88a959/amazon-ses-setup-the-things-nobody-tells-you-bounce-handling-dkim-iam-p39</link>
      <guid>https://dev.to/shreyash_1b88a959/amazon-ses-setup-the-things-nobody-tells-you-bounce-handling-dkim-iam-p39</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📖 Full guide with all code snippets and DNS records:&lt;/strong&gt; &lt;a href="https://www.devdecide.com/blogs/amazon-ses-setup" rel="noopener noreferrer"&gt;Amazon SES Setup — The Complete Guide&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Every Amazon SES tutorial on Google shows outdated screenshots and zero mention of what happens when your bounce rate spikes and AWS &lt;em&gt;silently kills your account&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This post covers the parts that actually bite you in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why SES? The Math Is Simple
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;100,000 emails/month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SendGrid&lt;/td&gt;
&lt;td&gt;~$90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon SES&lt;/td&gt;
&lt;td&gt;~$10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's not a rounding error. One order of magnitude cheaper. For a bootstrapped SaaS sending transactional emails, that compounds fast.&lt;/p&gt;

&lt;p&gt;The trade-off: AWS does not hold your hand. Misconfigure it and your emails land in spam — or your account gets permanently revoked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Understand the SES Sandbox
&lt;/h2&gt;

&lt;p&gt;Every new AWS account starts sandboxed. Hard limits apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Max &lt;strong&gt;200 emails per 24 hours&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Max &lt;strong&gt;1 email per second&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You can only send to &lt;strong&gt;pre-verified email addresses&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To escape the Sandbox, open a support case under &lt;strong&gt;Service Limit Increase → SES Sending Limits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Don't submit this ticket yet.&lt;/strong&gt; Build your bounce handling infrastructure first (Step 3). AWS will ask how you handle bounces — you need to already have an answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Verified Identity + DKIM
&lt;/h2&gt;

&lt;p&gt;Go to &lt;strong&gt;SES Console → Verified Identities → Create Identity → Domain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AWS generates 3 CNAME records for DKIM. Add them to your DNS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name:  token1._domainkey.yourdomain.com
Type:  CNAME
Value: token1.dkim.amazonses.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also add &lt;strong&gt;SPF&lt;/strong&gt; and &lt;strong&gt;DMARC&lt;/strong&gt; records. All three working together is what gives your domain a clean sender reputation from day one. Without DMARC, even a correctly DKIM-signed email can get flagged.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Configuration Sets — The Most Skipped Step
&lt;/h2&gt;

&lt;p&gt;This is where most developers fail. Here are the limits AWS enforces:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Probation&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Suspension&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bounce Rate&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complaint Rate&lt;/td&gt;
&lt;td&gt;0.08%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.1%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you hit 10% bounces, SES suspends you &lt;strong&gt;instantly with no warning&lt;/strong&gt;. Reactivating takes a manual support ticket and days of waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration Sets&lt;/strong&gt; are the fix. They route bounce and complaint events to an &lt;strong&gt;SNS topic → your webhook → your database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Setup in 5 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SES → Configuration Sets → Create → name it &lt;code&gt;production-transactional&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add destination → choose &lt;strong&gt;SNS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Hard Bounces&lt;/strong&gt; and &lt;strong&gt;Complaints&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Create SNS topic &lt;code&gt;ses-bounces-topic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add an HTTPS subscription pointing to your webhook URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every email you send must include &lt;code&gt;ConfigurationSetName&lt;/code&gt; in the API call. If you omit it, bounces are &lt;strong&gt;not tracked at all&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Sending Email (Node.js SDK v3)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SESClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SendEmailCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-ses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sesClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SESClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendTransactionalEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;htmlBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello@yourdomain.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ToAddresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;toAddress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UTF-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;htmlBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UTF-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ConfigurationSetName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production-transactional&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Never omit this&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendEmailCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sesClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same parameters map directly to &lt;strong&gt;Boto3 (Python)&lt;/strong&gt;, PHP, Ruby, or any official AWS SDK.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Handle the Bounce Webhook
&lt;/h2&gt;

&lt;p&gt;When SNS fires your webhook, it sends a &lt;code&gt;SubscriptionConfirmation&lt;/code&gt; POST first. You &lt;strong&gt;must&lt;/strong&gt; auto-confirm it or real events never arrive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;snsMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="c1"&gt;// Confirm subscription on first contact&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snsMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SubscriptionConfirmation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snsMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SubscribeURL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snsMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Notification&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sesEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snsMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sesEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bounce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;affected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sesEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bounce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bouncedRecipients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Blacklist these in your DB — never email them again&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then guard every send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;emailDeliverable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Skip blacklisted addresses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single check keeps your bounce rate clean at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  The IAM Policy (Least Privilege)
&lt;/h2&gt;

&lt;p&gt;Give your app credentials &lt;strong&gt;only&lt;/strong&gt; what they need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ses:SendEmail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ses:SendRawEmail"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No identity management. No billing access. If credentials leak, blast radius is contained.&lt;/p&gt;




&lt;h2&gt;
  
  
  4 Mistakes to Avoid
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Omitting &lt;code&gt;ConfigurationSetName&lt;/code&gt;&lt;/strong&gt; — AWS won't throw an error, but bounce tracking is dead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifying the wrong domain&lt;/strong&gt; — &lt;code&gt;mail.yourdomain.com&lt;/code&gt; ≠ &lt;code&gt;yourdomain.com&lt;/code&gt; in SES&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not confirming the SNS subscription&lt;/strong&gt; — bounce notifications never arrive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mismatched AWS regions&lt;/strong&gt; — your SESClient region must match where your identity was created&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  When SES Is NOT the Right Choice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Under 10,000 emails/month?&lt;/strong&gt; Use &lt;a href="https://resend.com" rel="noopener noreferrer"&gt;Resend&lt;/a&gt; or &lt;a href="https://postmarkapp.com" rel="noopener noreferrer"&gt;Postmark&lt;/a&gt; — the DX is smoother and cost difference is negligible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No backend experience on your team?&lt;/strong&gt; The SNS + IAM setup will cost more in debugging hours than you save&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need open rates, click tracking, A/B tests?&lt;/strong&gt; SES gives you raw events. You build the dashboards yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Come to SES when the invoices start to hurt.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;SES's reputation for being painful is earned — but the engineering discipline it demands is exactly what keeps your infrastructure cheap and stable at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;$10 per 100,000 sends&lt;/strong&gt; vs $90 elsewhere. Do the setup right once, and you never think about it again.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Want the full walkthrough&lt;/strong&gt; with every DNS record, the complete webhook code, and the exact console steps?&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://www.devdecide.com/blogs/amazon-ses-setup" rel="noopener noreferrer"&gt;Amazon SES Setup: The Complete Guide — DevDecide&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Amazon SES vs SendGrid: An Honest Comparison for SaaS Developers (2026)</title>
      <dc:creator>Shreyash</dc:creator>
      <pubDate>Tue, 24 Mar 2026 18:43:00 +0000</pubDate>
      <link>https://dev.to/shreyash_1b88a959/amazon-ses-vs-sendgrid-an-honest-comparison-for-saas-developers-2026-3k93</link>
      <guid>https://dev.to/shreyash_1b88a959/amazon-ses-vs-sendgrid-an-honest-comparison-for-saas-developers-2026-3k93</guid>
      <description>&lt;p&gt;If you search for &lt;strong&gt;Amazon SES vs SendGrid&lt;/strong&gt;, you'll get pages of generic listicles written by SEO agencies. Writers who have never deployed a server, authenticated a JWT, or dealt with a CORS error — writing reviews designed entirely to capture affiliate commissions.&lt;/p&gt;

&lt;p&gt;This comparison is different. It's based on actual SDK integration, real pricing math, and findings cross-referenced with engineers on Hacker News and Reddit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on my blog → &lt;a href="https://www.devdecide.com/comparisons/amazon-ses-vs-sendgrid" rel="noopener noreferrer"&gt;devdecide.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quick Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Amazon SES&lt;/strong&gt; wins on raw cost and AWS-native integration. &lt;strong&gt;SendGrid&lt;/strong&gt; wins on out-of-the-box developer experience, managed deliverability, and marketing tooling. Neither platform is universally better — and that's the honest answer no listicle will give you.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are These Tools, Actually?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Amazon SES&lt;/strong&gt; is bare-bones cloud email infrastructure from AWS. It gives you a reliable, pay-as-you-go sending engine — and absolutely nothing else. No dashboard. No visual editor. No bounce management UI. No automation workflows. Every feature beyond raw sending is something your engineering team has to build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SendGrid&lt;/strong&gt; (now owned by Twilio) is a full-featured email platform for both transactional and marketing emails. It ships with a template editor, automation workflows, advanced analytics, deliverability management, and a guided onboarding flow that gets teams sending in under an hour. The trade-off is cost — and at scale, that trade-off bites hard.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pricing: Where the Decision Gets Clear Fast
&lt;/h2&gt;

&lt;p&gt;Amazon SES charges &lt;strong&gt;$0.10 per 1,000 emails&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Volume&lt;/th&gt;
&lt;th&gt;Amazon SES&lt;/th&gt;
&lt;th&gt;SendGrid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100,000 emails/mo&lt;/td&gt;
&lt;td&gt;~$10&lt;/td&gt;
&lt;td&gt;~$89.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500,000 emails/mo&lt;/td&gt;
&lt;td&gt;~$53&lt;/td&gt;
&lt;td&gt;~$249.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000 emails/mo&lt;/td&gt;
&lt;td&gt;~$107.70&lt;/td&gt;
&lt;td&gt;~$399.95&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SES is roughly &lt;strong&gt;4–8x cheaper&lt;/strong&gt; at volume. But the sticker price isn't the full story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hidden SES costs to watch:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outgoing data transfer: $0.12/GB&lt;/li&gt;
&lt;li&gt;Virtual Deliverability Manager: can add up to 70% on top of base cost&lt;/li&gt;
&lt;li&gt;SNS notification charges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These typically add 10–15% on top of the base rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest math:&lt;/strong&gt; If you're sending 500,000+ emails/month and have the engineering bandwidth to operate AWS infrastructure, SES saves serious money. Under 100,000 emails/month, SendGrid's operational simplicity may be cheaper once you factor in engineering hours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Developer Experience: The Real Gap
&lt;/h2&gt;

&lt;p&gt;On &lt;strong&gt;SendGrid&lt;/strong&gt;, a backend developer can be sending authenticated transactional emails in under 30 minutes.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;Amazon SES&lt;/strong&gt;, a first-time setup involves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating and configuring an AWS account&lt;/li&gt;
&lt;li&gt;IAM roles and permission policies&lt;/li&gt;
&lt;li&gt;Domain verification and DKIM record setup&lt;/li&gt;
&lt;li&gt;Configuration Set creation &lt;em&gt;(critical — more on this below)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;SNS topic + webhook endpoint for bounce/complaint handling&lt;/li&gt;
&lt;li&gt;CloudWatch dashboards for monitoring&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these steps are impossible. But they represent hours of infrastructure work before a single email gets sent.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Comparison: Side by Side
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sending with Amazon SES (AWS SDK v3)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SESv2Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SendEmailCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-sesv2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SESv2Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendEmailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;FromEmailAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noreply@yourdomain.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ToAddresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Simple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your order has shipped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UTF-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;p&amp;gt;Your order &amp;lt;b&amp;gt;#12345&amp;lt;/b&amp;gt; is on its way.&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UTF-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your order #12345 is on its way.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UTF-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;ConfigurationSetName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-config-set&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ⚠️ Never omit this&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Message ID:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; Always pass &lt;code&gt;ConfigurationSetName&lt;/code&gt;. Without it, bounces go untracked and AWS can suspend your sending account without warning.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Sending with SendGrid (Node.js SDK)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sendgrid/mail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setApiKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SENDGRID_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noreply@yourdomain.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your order has shipped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your order #12345 is on its way.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;p&amp;gt;Your order &amp;lt;b&amp;gt;#12345&amp;lt;/b&amp;gt; is on its way.&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup time delta: SendGrid reaches first successful send in ~15 minutes. Amazon SES requires IAM config, domain verification, DKIM setup, and Configuration Set creation — expect 1–3 hours minimum for a production-ready integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rate Limits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Amazon SES
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Sandbox&lt;/th&gt;
&lt;th&gt;Production&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sending rate&lt;/td&gt;
&lt;td&gt;1 email/sec&lt;/td&gt;
&lt;td&gt;14 emails/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily quota&lt;/td&gt;
&lt;td&gt;200 emails/day&lt;/td&gt;
&lt;td&gt;50,000 emails/day&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;New SES accounts start in &lt;strong&gt;sandbox mode&lt;/strong&gt; — you can only send to verified addresses. Production access requires a manual limit increase request (24–48 hour approval).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Handling throttling with exponential backoff:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SESv2Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// AWS SDK v3 handles ThrottlingException retries automatically&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendWithRateLimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ratePerSecond&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ratePerSecond&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendEmailCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SendGrid
&lt;/h3&gt;

&lt;p&gt;All paid plans enforce &lt;strong&gt;600 requests/minute&lt;/strong&gt;. The optimization most developers miss: batch up to &lt;strong&gt;1,000 recipients per API call&lt;/strong&gt; using &lt;code&gt;personalizations&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;personalizations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;dynamicTemplateData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noreply@yourdomain.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d-xxxxxxxxxxxxxxxxxxxx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;personalizations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Up to 1,000 recipients per request&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Batching 1,000 recipients per request reduces API calls by 1,000x and keeps you well within the rate limit ceiling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deliverability Benchmarks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Avg Inbox Rate&lt;/th&gt;
&lt;th&gt;Gmail&lt;/th&gt;
&lt;th&gt;Outlook&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SES (dedicated IP)&lt;/td&gt;
&lt;td&gt;95–98%&lt;/td&gt;
&lt;td&gt;96–99%&lt;/td&gt;
&lt;td&gt;94–97%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES (shared IP)&lt;/td&gt;
&lt;td&gt;88–94%&lt;/td&gt;
&lt;td&gt;89–95%&lt;/td&gt;
&lt;td&gt;86–92%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SendGrid (dedicated IP, Pro)&lt;/td&gt;
&lt;td&gt;95–98%&lt;/td&gt;
&lt;td&gt;95–98%&lt;/td&gt;
&lt;td&gt;93–97%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SendGrid (shared IP, Essentials)&lt;/td&gt;
&lt;td&gt;85–93%&lt;/td&gt;
&lt;td&gt;87–94%&lt;/td&gt;
&lt;td&gt;84–91%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At peak configuration, both platforms deliver equivalent inbox rates. The difference is the &lt;strong&gt;operational cost of reaching that peak&lt;/strong&gt; — SendGrid handles it for you, SES requires you to build it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication Setup That Actually Moves the Needle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPF:   v=spf1 include:amazonses.com ~all          (SES)
       v=spf1 include:sendgrid.net ~all             (SendGrid)

DKIM:  2048-bit key recommended (both support it)
       Rotate every 6–12 months for high-volume senders

DMARC: v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@yourdomain.com
       Start with p=none → graduate to p=quarantine after 30 days of clean reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Skipping DMARC costs roughly 3–8% inbox placement at major providers in 2026. It is no longer optional for serious senders.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AWS Will Suspend You at These Thresholds
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Warning&lt;/th&gt;
&lt;th&gt;Suspension&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bounce rate&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complaint rate&lt;/td&gt;
&lt;td&gt;0.08%&lt;/td&gt;
&lt;td&gt;0.10%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SendGrid surfaces these in a dashboard automatically. SES requires you to wire up CloudWatch alarms yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AWS CDK — bounce rate alarm&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounceRateAlarm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Alarm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SESBounceRateAlarm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Metric&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AWS/SES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reputation.BounceRate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;statistic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Average&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Alert at 5% — before the 10% suspension threshold&lt;/span&gt;
  &lt;span class="na"&gt;evaluationPeriods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;comparisonOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GREATER_THAN_OR_EQUAL_TO_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Bounce Handling: The Biggest Operational Difference
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SendGrid&lt;/strong&gt; handles this for you. Bounce happens → SendGrid catches it, logs it, suppresses the address, surfaces it in analytics. Zero engineering work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon SES&lt;/strong&gt; requires you to build this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up an SNS topic&lt;/li&gt;
&lt;li&gt;Create a Configuration Set pointing to that SNS topic&lt;/li&gt;
&lt;li&gt;Build and host a webhook endpoint that processes bounce events&lt;/li&gt;
&lt;li&gt;Suppress bad addresses in your own database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If that SNS webhook goes down when SES fires a bounce event, SNS retries for up to 23 days — but your bounce rate keeps climbing during that window. That webhook deserves the same uptime monitoring as your production API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Use Each?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose Amazon SES if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're already inside the AWS ecosystem (EC2, Lambda, RDS)&lt;/li&gt;
&lt;li&gt;You're sending 500,000+ emails/month where the cost delta is significant&lt;/li&gt;
&lt;li&gt;You have dedicated backend ownership to build bounce/complaint infrastructure&lt;/li&gt;
&lt;li&gt;Email is a pure utility — receipts, alerts, verification codes — with no marketing component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose SendGrid if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to ship fast without building email infrastructure from scratch&lt;/li&gt;
&lt;li&gt;You need both transactional email and marketing campaigns in one platform&lt;/li&gt;
&lt;li&gt;You're frontend-heavy or non-technical and don't own backend infrastructure&lt;/li&gt;
&lt;li&gt;You're sending under 500,000 emails/month where the convenience premium is worth it&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Amazon SES&lt;/strong&gt; is the right choice when cost is the primary constraint and your engineering team has the bandwidth to build and operate email infrastructure inside AWS. At scale, the pricing difference is not marginal — it is substantial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SendGrid&lt;/strong&gt; is the right choice when speed of implementation, managed deliverability, and built-in analytics matter more than squeezing every cent out of sending cost. For most early-stage SaaS products, that trade-off is worth it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Full comparison with additional benchmarks originally published on my blog → [&lt;a href="https://www.devdecide.com/comparisons/amazon-ses-vs-sendgrid" rel="noopener noreferrer"&gt;https://www.devdecide.com/comparisons/amazon-ses-vs-sendgrid&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pricing figures sourced from official AWS and SendGrid documentation. Last updated: March 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sendgrid</category>
      <category>email</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
