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.
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.
| Nodemailer | Resend | SendPigeon | |
|---|---|---|---|
| What it is | SMTP client library | Transactional email API | Full email platform |
| Protocol | SMTP | HTTP API | HTTP API + SMTP relay |
| Sequences | No | No | Yes |
| Broadcasts | No | Basic | Full (contacts, tags) |
| Inbound | No | No | Yes |
| Local dev | Manual setup | No | Built-in CLI |
| Price | Free (+ 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
| Feature | Nodemailer | Resend | SendPigeon |
|---|---|---|---|
| Send email | SMTP | API | API + SMTP relay |
| DKIM/SPF | Manual DNS + key rotation | Automatic | Automatic |
| Bounce handling | Build it yourself | Built-in | Built-in |
| Analytics | None | Opens, clicks | Opens, clicks, deliverability |
| Webhooks | None | Delivery events | Delivery + inbound events |
| Templates | None (use React Email, etc.) | React Email integration | Visual editor + API |
| Sequences | No | No | Full API (5 step types) |
| Broadcasts | No | Basic (Audiences) | Full (contacts, tags, targeting) |
| Inbound parsing | No | No | MX → webhook |
| Local dev server | Manual (Mailpit, etc.) | No | npx @sendpigeon-sdk/cli dev |
| SDKs | Node.js only | Node, Python, Go, PHP, Ruby, Elixir, Java, .NET | Node, Python, Go, PHP |
| Serverless/Edge | Connection issues | Works everywhere | Works everywhere |
| Free tier | N/A (library) | 3K emails/mo | 3K 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
| Volume | Nodemailer + SES | Resend | SendPigeon |
|---|---|---|---|
| 3K/mo | ~$0.30 | Free | Free |
| 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
- SendPigeon vs Nodemailer — detailed two-way comparison
- SendPigeon vs Resend — pricing deep-dive
- Resend alternatives — full alternatives overview
- Drip email API — build sequences in code
- Send email in Next.js — framework guide
- Get started with SendPigeon — free, no credit card