Back to blog
NodemailerResendComparisonNode.jsEmail API

Nodemailer vs Resend vs SendPigeon: Which Should You Use?

A three-way comparison of Nodemailer, Resend, and SendPigeon for sending email from Node.js. SMTP vs API, features, pricing, and when to use each.

SendPigeon TeamApril 24, 20266 min read

Three popular ways to send email from Node.js. Nodemailer is the raw SMTP library. Resend and SendPigeon are email APIs. Each solves a different problem.

TL;DR
NodemailerResendSendPigeon
What it isSMTP client libraryTransactional email APIFull email platform
ProtocolSMTPHTTP APIHTTP API + SMTP relay
SequencesNoNoYes
BroadcastsNoBasicFull (contacts, tags)
InboundNoNoYes
Local devManual setupNoBuilt-in CLI
PriceFree (+ SMTP costs)$20/mo (50K)$10/mo (10K)

Use Nodemailer for raw SMTP control. Use Resend for clean transactional email. Use SendPigeon for transactional + sequences + broadcasts + inbound.


The Three Approaches

Nodemailer: The SMTP Library

Nodemailer sends emails over SMTP. It's been around since 2010, has 17K+ GitHub stars, and works with any SMTP server.

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "smtp.gmail.com",
  port: 587,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

await transporter.sendMail({
  from: "app@yourdomain.com",
  to: "user@example.com",
  subject: "Welcome!",
  html: "<p>Thanks for signing up.</p>",
});

You manage: SMTP server, DKIM/SPF setup, bounce processing, reputation monitoring, connection pooling, retry logic.

Resend: The Clean API

Resend is an email API focused on developer experience. Modern docs, React Email integration, simple SDK.

import { Resend } from "resend";

const resend = new Resend("re_xxx");

await resend.emails.send({
  from: "app@yourdomain.com",
  to: "user@example.com",
  subject: "Welcome!",
  html: "<p>Thanks for signing up.</p>",
});

Resend manages: Deliverability, DKIM/SPF, bounce processing, analytics.

SendPigeon: The Full Platform

SendPigeon is an email API plus sequences, broadcasts, inbound parsing, and a local dev server.

import { SendPigeon } from "sendpigeon";

const pigeon = new SendPigeon("sp_live_xxx");

await pigeon.emails.send({
  from: "app@yourdomain.com",
  to: "user@example.com",
  subject: "Welcome!",
  html: "<p>Thanks for signing up.</p>",
});

SendPigeon manages: Everything Resend does, plus sequences, broadcasts, inbound email, and local dev.


Feature Comparison

FeatureNodemailerResendSendPigeon
Send emailSMTPAPIAPI + SMTP relay
DKIM/SPFManual DNS + key rotationAutomaticAutomatic
Bounce handlingBuild it yourselfBuilt-inBuilt-in
AnalyticsNoneOpens, clicksOpens, clicks, deliverability
WebhooksNoneDelivery eventsDelivery + inbound events
TemplatesNone (use React Email, etc.)React Email integrationVisual editor + API
SequencesNoNoFull API (5 step types)
BroadcastsNoBasic (Audiences)Full (contacts, tags, targeting)
Inbound parsingNoNoMX → webhook
Local dev serverManual (Mailpit, etc.)Nonpx @sendpigeon-sdk/cli dev
SDKsNode.js onlyNode, Python, Go, PHP, Ruby, Elixir, Java, .NETNode, Python, Go, PHP
Serverless/EdgeConnection issuesWorks everywhereWorks everywhere
Free tierN/A (library)3K emails/mo3K emails/mo

Sending Code: Side by Side

Nodemailer

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "smtp.example.com",
  port: 587,
  secure: false,
  auth: { user: "xxx", pass: "xxx" },
});

const info = await transporter.sendMail({
  from: '"App" <app@yourdomain.com>',
  to: "user@example.com",
  subject: "Hello",
  html: "<p>Hello!</p>",
  attachments: [{ filename: "invoice.pdf", path: "./invoice.pdf" }],
});

Resend

import { Resend } from "resend";
const resend = new Resend("re_xxx");

const { data, error } = await resend.emails.send({
  from: "App <app@yourdomain.com>",
  to: "user@example.com",
  subject: "Hello",
  html: "<p>Hello!</p>",
  attachments: [{ filename: "invoice.pdf", path: "./invoice.pdf" }],
});

SendPigeon

import { SendPigeon } from "sendpigeon";
const pigeon = new SendPigeon("sp_live_xxx");

const { data, error } = await pigeon.emails.send({
  from: "App <app@yourdomain.com>",
  to: "user@example.com",
  subject: "Hello",
  html: "<p>Hello!</p>",
  attachments: [{ filename: "invoice.pdf", path: "./invoice.pdf" }],
});

The API sending code is nearly identical between Resend and SendPigeon. The difference is what you get beyond sending.


The Serverless Problem with Nodemailer

Nodemailer opens SMTP connections. In serverless environments (AWS Lambda, Vercel Edge, Cloudflare Workers), this causes issues:

  • Cold starts — SMTP connection handshake adds latency
  • Connection limits — Lambda functions can exhaust SMTP connections
  • Edge runtime — SMTP requires TCP sockets, which edge runtimes don't support

Email APIs use HTTP, which works everywhere serverless runs. If you deploy to Vercel, Lambda, or any edge environment, Nodemailer is a poor fit.


When You Actually Need Sequences

A SaaS product's email needs evolve:

Day 1: Password reset, email verification (transactional) Month 3: Onboarding drip emails (sequences) Month 6: Product updates, newsletters (broadcasts) Month 9: Reply-by-email, support tickets (inbound)

With Nodemailer, you need to add each capability separately. With Resend, you add a separate sequences tool (Customer.io, Loops) when you need drips. With SendPigeon, it's all there from the start — one SDK, one dashboard, one bill.


Pricing

VolumeNodemailer + SESResendSendPigeon
3K/mo~$0.30FreeFree
10K/mo$1$20$10
50K/mo$5$20$15
100K/mo$10$90$39

Nodemailer + SES is the cheapest if you don't count your time. You manage DNS, bounce handling, complaint processing, and reputation yourself.

SendPigeon is cheaper than Resend at every paid tier, and includes features Resend charges nothing for because they don't exist (sequences, inbound).


Decision Matrix

Choose Nodemailer if:

  • You run your own mail server or have strong SMTP infrastructure
  • You need fine-grained SMTP control (custom headers, raw MIME)
  • You're building a mail client or relay, not a web app
  • Cost is the only priority and you have ops capacity

Choose Resend if:

  • You only need transactional email
  • You value the cleanest developer experience
  • You use React Email for templates
  • You need Ruby, Elixir, Java, or .NET SDKs

Choose SendPigeon if:

  • You need transactional + sequences + broadcasts
  • You want inbound email parsing
  • You have multiple domains
  • You want local dev testing without Docker
  • You want one platform instead of Resend + Customer.io + Mailchimp

Migrating from Nodemailer

Switching from Nodemailer to either API is straightforward:

// Before: Nodemailer
const transporter = nodemailer.createTransport({ host: "...", auth: {...} });
await transporter.sendMail({ from, to, subject, html });

// After: SendPigeon (or Resend — nearly identical)
const pigeon = new SendPigeon("sp_live_xxx");
await pigeon.emails.send({ from, to, subject, html });

Remove the SMTP config, add an API key. Your send calls look the same.


Next Steps