What Is SMTP? A Developer's Guide
Learn what SMTP is, how it works, which ports to use, and when to choose SMTP vs an email API. Includes code examples for sending email via SMTP in Node.js and Python.
SMTP (Simple Mail Transfer Protocol) is how email gets sent across the internet. When you hit "send", your email client talks to an SMTP server, which routes the message to the recipient's server. This guide covers how SMTP works, which ports to use, and when to choose SMTP vs an email API.
SMTP in 30 seconds:
- SMTP is the protocol for sending email (IMAP/POP3 are for receiving)
- Use port 587 (STARTTLS) or 465 (implicit TLS) — never port 25
- For modern apps, an email API is simpler than raw SMTP
- SMTP still matters for legacy systems, WordPress, and local testing
How SMTP Works
When you send an email, here's what happens:
Your App → SMTP Server → Recipient's Mail Server → Inbox
Step by step:
- Connect — Your app opens a TCP connection to an SMTP server
- Handshake — The server responds with a greeting. Your app sends
EHLOto identify itself - TLS — If using STARTTLS (port 587), encryption is negotiated
- Authenticate — Your app sends credentials (username + password or API key)
- Envelope — Your app specifies the sender (
MAIL FROM) and recipient (RCPT TO) - Data — Your app sends the email content (headers, body, attachments)
- Queue — The SMTP server accepts the message and queues it for delivery
- Delivery — The server looks up the recipient's MX records and forwards the message
- Close — Connection is closed
Here's what the raw SMTP conversation looks like:
S: 220 smtp.example.com ESMTP
C: EHLO myapp.com
S: 250-smtp.example.com Hello
S: 250-STARTTLS
S: 250 OK
C: STARTTLS
S: 220 Ready to start TLS
(TLS handshake happens)
C: AUTH LOGIN
S: 334 VXNlcm5hbWU6
C: (base64 username)
S: 334 UGFzc3dvcmQ6
C: (base64 password)
S: 235 Authentication successful
C: MAIL FROM:<hello@myapp.com>
S: 250 OK
C: RCPT TO:<user@example.com>
S: 250 OK
C: DATA
S: 354 Start mail input
C: Subject: Hello
C: From: hello@myapp.com
C: To: user@example.com
C:
C: Hello, this is a test email.
C: .
S: 250 OK: queued
C: QUIT
S: 221 Bye
SMTP Ports
| Port | Protocol | Use Case |
|---|---|---|
| 587 | STARTTLS | Recommended. Starts unencrypted, upgrades to TLS. Standard for email submission. |
| 465 | Implicit TLS | Connection is encrypted from the start. Re-standardized in RFC 8314. |
| 25 | Plaintext / STARTTLS | Server-to-server relay only. Blocked by most ISPs and cloud providers (AWS, GCP, Azure). |
| 2525 | STARTTLS | Unofficial alternative when 587 is blocked. Same behavior as 587. |
Never use port 25 from applications. It's blocked by most cloud providers and ISPs to prevent spam. Use port 587 or 465.
Which port should I use?
Port 587 with STARTTLS is the standard choice. It's supported everywhere and is the most compatible option. Port 465 with implicit TLS is also fine — it skips the STARTTLS upgrade step since the connection is encrypted from the start.
SMTP vs Email API
| SMTP | Email API | |
|---|---|---|
| Protocol | TCP connection with SMTP commands | Single HTTP POST request |
| Authentication | Username + password | API key in header |
| Connection | Persistent TCP connection | Stateless HTTP request |
| Serverless | Doesn't work well | Works perfectly |
| Tracking | Not built-in | Open/click tracking included |
| Error handling | SMTP status codes, retry logic | JSON responses, automatic retries |
| Setup complexity | Connection pooling, timeouts, TLS | One HTTP call |
When to use SMTP
- Legacy systems — WordPress, older CMS platforms, and enterprise software that only support SMTP
- Email clients — Desktop/mobile apps like Thunderbird or Outlook
- Local development — Catching emails with a local SMTP server
- Migration — Transitioning from an existing SMTP setup without code changes
When to use an email API
- Modern web apps — Any application built with Node.js, Python, Go, etc.
- Serverless — Lambda, Vercel, Supabase Edge Functions
- Transactional email — Password resets, notifications, receipts
- When you need tracking — Open rates, click rates, delivery status
For most developers, an email API is the better choice. It's simpler, works in serverless, and includes tracking. See our email API comparison for options.
Send Email via SMTP in Node.js
Using Nodemailer with an SMTP server:
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: "smtp.yourprovider.com",
port: 587,
secure: false, // STARTTLS
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
const info = await transporter.sendMail({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Hello via SMTP",
html: "<h1>Hello!</h1><p>Sent via SMTP.</p>",
});
console.log("Message sent:", info.messageId);
Nodemailer is Node.js only. It doesn't work in Edge runtimes, Cloudflare Workers, or Deno because it needs raw TCP sockets. If you're deploying to these environments, use an HTTP-based email API instead.
The API alternative
Compare the SMTP approach above with an email API call:
import { SendPigeon } from "sendpigeon";
const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);
const { data, error } = await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Hello via API",
html: "<h1>Hello!</h1><p>Sent via API.</p>",
});
No connection management, no transport configuration, works everywhere.
Send Email via SMTP in Python
Using smtplib (Python standard library):
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
msg = MIMEMultipart()
msg["From"] = "hello@yourdomain.com"
msg["To"] = "user@example.com"
msg["Subject"] = "Hello via SMTP"
html = "<h1>Hello!</h1><p>Sent via SMTP from Python.</p>"
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP("smtp.yourprovider.com", 587) as server:
server.starttls()
server.login(os.environ["SMTP_USER"], os.environ["SMTP_PASS"])
server.sendmail(
"hello@yourdomain.com",
"user@example.com",
msg.as_string(),
)
The API alternative
import os
from sendpigeon import SendPigeon
client = SendPigeon(os.environ["SENDPIGEON_API_KEY"])
result = client.send(
from_="hello@yourdomain.com",
to="user@example.com",
subject="Hello via API",
html="<h1>Hello!</h1><p>Sent via API from Python.</p>",
)
Common SMTP Providers
If you need SMTP credentials (for WordPress, legacy systems, etc.), these providers offer SMTP relay:
| Provider | SMTP Host | Port | Notes |
|---|---|---|---|
| SendPigeon | smtp.sendpigeon.dev | 587 | Included in all plans |
| SendGrid | smtp.sendgrid.net | 587 | API key as password |
| Postmark | smtp.postmarkapp.com | 587 | Server token as password |
| Mailgun | smtp.mailgun.org | 587 | Domain-specific credentials |
| Amazon SES | email-smtp.{region}.amazonaws.com | 587 | IAM credentials |
| Gmail | smtp.gmail.com | 587 | App password required, 500/day limit |
Gmail SMTP is not meant for applications. It has a 500 emails/day limit and requires an app-specific password. Use a dedicated email service instead.
Local SMTP for Development
For local development, use a local SMTP server to catch emails without sending them to real inboxes.
SendPigeon CLI
npx @sendpigeon-sdk/cli dev
Starts a local SMTP server on localhost:4125. All emails are caught and viewable at localhost:4100. Zero install, no Docker.
Point your app's SMTP config at it:
SMTP_HOST=localhost
SMTP_PORT=4125
See our full guide on local email testing and MailHog alternatives for more options.
Troubleshooting SMTP Errors
Connection refused
Error: connect ECONNREFUSED 127.0.0.1:587
The SMTP server isn't reachable. Check the host, port, and that no firewall is blocking the connection. If using a cloud provider, port 25 is likely blocked — use 587 or 465.
Authentication failed
535 5.7.8 Authentication credentials invalid
Wrong username or password. Double-check your credentials. For services that use API keys as passwords (like SendGrid), make sure you're using the API key, not your account password.
TLS handshake failure
Error: unable to verify the first certificate
The server's TLS certificate can't be verified. This usually happens with self-signed certificates in development. For production, use a provider with valid certificates.
Connection timeout
Error: Connection timed out
The server didn't respond in time. Check if the port is open and the host is correct. Port 25 timeouts often mean the port is blocked by your ISP or cloud provider.
Message rejected
550 5.7.1 Relaying denied
The server won't relay your message. This happens when you try to send through a server you're not authenticated with, or when the server doesn't handle email for the recipient's domain.
Frequently Asked Questions
What is SMTP?
SMTP (Simple Mail Transfer Protocol) is the internet standard for sending email. Your email client or application connects to an SMTP server, authenticates, and hands off the message. The server then routes it to the recipient.
What is the difference between SMTP and IMAP?
SMTP is for sending email. IMAP (and POP3) is for receiving and reading email. When you compose and send, your app uses SMTP. When you check your inbox, your app uses IMAP.
What port does SMTP use?
Port 587 with STARTTLS is the standard for email submission from applications. Port 465 uses implicit TLS. Port 25 is for server-to-server relay and is blocked by most cloud providers and ISPs.
Should I use SMTP or an email API?
Use an email API for most modern applications. It's simpler (one HTTP call), works in serverless environments, and includes built-in tracking. Use SMTP only when your framework requires it (WordPress, legacy systems) or for local development testing.
Is SMTP secure?
The SMTP protocol itself is plaintext. Modern implementations add security with TLS — either STARTTLS (port 587) which upgrades an unencrypted connection, or implicit TLS (port 465) which encrypts from the start. Always use TLS; never send email over unencrypted SMTP.
Can I use SMTP in serverless functions?
Not well. SMTP requires persistent TCP connections, which conflict with serverless environments that have short-lived execution contexts and restricted networking. Use an HTTP-based email API in serverless — it's a single request with no connection management.
Next Steps
- Compare email APIs for developers
- Set up DKIM, SPF, and DMARC on your domain
- Learn what is an email domain
- Review the email deliverability checklist
- Send a test email to verify your setup