Client Area

Email Services Compared — SendGrid, AWS SES, Mailgun, Postmark, Resend

ByDomain India Team·DomainIndia Engineering
6 min readPublished 22 Apr 2026Updated 4 Jun 2026952 views

In this article

  • 1Why not just use SMTP from your app?
  • 2The five major options
  • 3Decision tree
  • 4Deliverability — the real differentiator
  • 5Setup — all five are similar

Email Services Compared — SendGrid, AWS SES, Mailgun, Postmark, Resend

TL;DR
Sending email from your DomainIndia-hosted app via SMTP is fine for occasional use. Anything transactional (receipts, OTPs, password resets) needs a dedicated email API — better deliverability, dashboards, suppression lists. This guide compares the five major options by price, deliverability, API quality, and India-market features.

Why not just use SMTP from your app?

Options:

  1. PHP `mail()` / Node's nodemailer → SMTP via localhost — poor deliverability, emails land in spam
  2. Direct SMTP to Gmail — daily limits, no dashboards, Gmail bans apps that spam
  3. DomainIndia email hosting — good for small volumes (newsletters), but transactional needs dedicated infra
  4. Email API (SendGrid, SES, etc.) — purpose-built for transactional, best deliverability

For transactional email (>100/day), use an API. Your IP is shared with thousands of good senders; dedicated IPs available for high volumes.

The five major options

ServiceFree tierPaid fromBest forIndia notes
SendGrid (Twilio)100 emails/day$19.95/mo (50K)General-purposeGood Indian card support
AWS SES200 emails/day (in EC2)$0.10 per 1KCost-sensitive, AWS ecosystemLower per-email cost
Mailgun100 emails/day (3 mo)$15/mo (10K)Developers, complex routingMid-tier
Postmark100 emails/month$15/mo (10K)Transactional purist (no marketing)Premium deliverability
Resend3K/mo, 100/day$20/mo (50K)Modern DX, React email templatesNewest, great API

Decision tree

"Just want it to work, don't care much":

  • Small volume: Resend (best DX, 3K/mo free)
  • Medium: SendGrid or Mailgun
  • High volume: AWS SES (cheapest)

"Best inbox placement, won't compromise":

  • Postmark — industry-leading deliverability, transactional-only policy

"Already use AWS / cost-sensitive":

  • AWS SES — $0.10/1K is unbeatable

"Want to include React/HTML templates":

  • Resend — ships with react-email integration

"Need SMS + Email + WhatsApp unified":

  • SendGrid (Twilio owned) or Mailgun + Twilio

Deliverability — the real differentiator

Same SPF/DKIM/DMARC setup, same mail content — different services have different delivery rates.

Approximate inbox placement (2026, based on public reports):

ServiceGmail inbox %Outlook inbox %India local ISPs %
Postmark99%95%93%
Resend97%93%91%
SendGrid96%92%90%
Mailgun95%91%88%
AWS SES93%88%85% (improves with dedicated IP)

Margins matter at scale — 2% difference on 1M emails = 20,000 missed customers.

Setup — all five are similar

Pattern:

  1. Sign up + verify domain ownership
  2. Add SPF + DKIM DNS records they provide
  3. Get API key
  4. Send via HTTPS POST
  5. Configure webhooks for delivery events + bounces

Example — Resend (simplest)

bash
npm install resend
typescript
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  html: '<p>Thanks for signing up</p>',
});

Example — SendGrid

bash
npm install @sendgrid/mail
typescript
import sgMail from '@sendgrid/mail';

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

await sgMail.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  html: '<p>Thanks for signing up</p>',
});

Example — AWS SES

bash
npm install @aws-sdk/client-sesv2
typescript
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';

const ses = new SESv2Client({ region: 'ap-south-1' });

await ses.send(new SendEmailCommand({
  FromEmailAddress: '[email protected]',
  Destination: { ToAddresses: ['[email protected]'] },
  Content: {
    Simple: {
      Subject: { Data: 'Welcome!' },
      Body: { Html: { Data: '<p>Thanks!</p>' } },
    },
  },
}));

Template management

Three approaches:

1. In-code templates (Resend + react-email):

tsx
import { Html, Head, Body, Heading } from '@react-email/components';

export function WelcomeEmail({ name }) {
  return (
    <Html>
      <Head />
      <Body>
        <Heading>Welcome {name}!</Heading>
      </Body>
    </Html>
  );
}

await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  react: <WelcomeEmail name="Rajesh" />,
});

Component-driven, previewable, testable.

2. Provider-hosted templates (SendGrid, Mailgun):

Create template in dashboard; reference by ID:

typescript
await sgMail.send({
  templateId: 'd-abc123...',
  dynamicTemplateData: { name: 'Rajesh', orderId: 'ORD-1234' },
});

Marketing team can edit without deploys.

3. MJML + Nunjucks (self-hosted):

MJML compiles to responsive HTML. Template in git, render at send time. See mjml.io.

Webhooks — track what happens

Every service sends webhooks for:

  • delivered — receiver accepted
  • opened — user opened (requires tracking pixel)
  • clicked — user clicked link
  • bounced — undeliverable (hard vs soft)
  • complained — user marked as spam
  • unsubscribed — user opted out

Handler:

typescript
app.post('/webhook/email', express.raw({ type: 'application/json' }),
  (req, res) => {
    // Verify signature (per provider)
    const events = JSON.parse(req.body);
    for (const e of events) {
      if (e.event === 'bounce') {
        // Add to suppression list
        db.suppressionList.create({ email: e.email, reason: 'bounce' });
      }
      if (e.event === 'spamreport') {
        db.user.update({ where: { email: e.email }, data: { suppressed: true } });
      }
    }
    res.sendStatus(200);
  }
);

Critical: never send to suppressed emails. Bounce rate >5% damages your domain reputation.

India-specific notes

  • Mobile numbers for email verification — some Indian apps require phone + email; plan for both
  • Hindi/regional language emails — all providers support UTF-8; some providers' templates have RTL/Hindi rendering issues — test
  • GST on bills — most providers bill from US; your accountant may need GST self-reverse-charge handling
  • Card payments — Razorpay doesn't work for subscription to US services; Indian international cards work but watch for 3D Secure friction

Dedicated IP — when to buy

Dedicated IP: ₹8,000-₹20,000/mo add-on. Consider when:

  • Volume >500K/mo
  • You need predictable deliverability (shared IPs have mixed reputation)
  • Scheduled bulk sends (newsletters)

Warm up dedicated IP slowly — jumping from 0 to 100K/day triggers spam filters.

Cost comparison (at 100K/month)

ServiceMonthly cost (100K emails)Extras
SendGrid Essentials$19.95Validation API
AWS SES$10 (100K × $0.10/1K)In ap-south-1 region
Mailgun Foundation$35Log retention
Postmark$15 (10K batch pack) — or $50 for 100KTransactional-only
Resend Pro$20Modern DX

For 100K/month, AWS SES wins on cost. Postmark wins on deliverability.

Common pitfalls

FAQ

Q Can I use DomainIndia email hosting for transactional?

For ≤100 emails/day — fine. Above that, deliverability drops on shared infrastructure. Use a dedicated transactional API.

Q Best service for OTPs?

Speed matters — Postmark delivers in 1-2 seconds. SendGrid and Resend similar. Avoid SES for OTPs (slightly higher latency on cold calls).

Q Multiple services for redundancy?

Advanced pattern: primary + fallback. Use Resend; fall back to SendGrid on API outage. Libraries like node-mailer-multi support this.

Q SMS instead of email?

India SMS ₹0.20-0.50 per message (DLT-registered), email ₹0.02-0.05. SMS faster, email cheaper. Use SMS for critical OTPs; email for everything else.

Q Do I need separate sender for marketing vs transactional?

Yes — use noreply@ or alerts@ for transactional, news@ or subdomain for marketing. Isolates reputation if marketing gets flagged.

Send transactional email reliably from your DomainIndia-hosted app. View hosting plans

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket