SendGrid vs AWS SES
SendGrid versus Amazon SES is usually a cost-versus-convenience decision. SES wins on raw price by an order of magnitude and integrates natively with IAM, CloudWatch, and the rest of AWS — if your infrastructure already lives there, sending through SES keeps billing and auth in one place. What SES does not give you is a product: no template marketing tools, minimal UI, and deliverability management is your job. SendGrid charges more per message but bundles IP management, marketing email, template editing, and analytics into one platform. In the message model SendGrid supports per-message metadata and native scheduled sending; SES supports neither, so the SDK's field-support checks matter when SES is your fallback — tags are the only categorization that survives the hop.
Message field support
Field support as encoded in Email SDK's own adapter capability matrix — the same data the SDK uses to reject sends that would silently drop fields.
| Message field | SendGrid | AWS SES |
|---|---|---|
| CC recipients | ✓ | ✓ |
| BCC recipients | ✓ | ✓ |
| Reply-To address | ✓ | ✓ |
| Custom headers | ✓ | ✓ |
| Attachments | ✓ | ✓ |
| Tags | ✓ | ✓ |
| Metadata | ✓ | — |
| Scheduled sending (sendAt) | ✓ | — |
Fallback compatibility
Failing over from SendGrid to AWS SES loses: metadata, scheduled sending (sendat). Email SDK rejects the fallback send for messages that use these fields instead of dropping them silently.
Same code, either provider
With Email SDK the send call is identical for both providers — only the adapter import changes:
import { createEmailClient } from "@opencoredev/email-sdk";
import { sendgrid } from "@opencoredev/email-sdk/sendgrid";
const client = createEmailClient({
adapters: [sendgrid({ apiKey: process.env.SENDGRID_API_KEY! })],
});
await client.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Welcome!",
html: "<p>It works.</p>",
});Or run both, with automatic fallback:
import { createEmailClient } from "@opencoredev/email-sdk";
import { sendgrid } from "@opencoredev/email-sdk/sendgrid";
import { ses } from "@opencoredev/email-sdk/ses";
const client = createEmailClient({
adapters: [
sendgrid({ apiKey: process.env.SENDGRID_API_KEY! }),
ses({
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: "us-east-1",
}),
],
defaultAdapter: "sendgrid",
fallback: ["ses"],
});FAQ
- Can I switch from SendGrid to AWS SES without rewriting my email code?
- Yes. With Email SDK both providers share one typed send() call and one message shape, so switching from SendGrid to AWS SES is a one-line adapter change plus an API key. The SDK fails fast if a message uses a field AWS SES does not support, so nothing is silently dropped.
- Which message fields does SendGrid support that AWS SES doesn't?
- SendGrid supports metadata, scheduled sending (sendat) in the unified message shape, which AWS SES does not.
- Can I use AWS SES as a fallback for SendGrid?
- Partially. Email SDK checks field support before every send: a fallback from SendGrid to AWS SES is rejected for messages using metadata, scheduled sending (sendat). Messages that avoid those fields fail over cleanly.
Adapter docs
Setup guides: SendGrid adapter and AWS SES adapter. Sending from your own domain? Verify SPF, DKIM, and DMARC with the free email DNS checker.