Back to blog
SMTPEmailDeveloper GuideTutorial

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.

SendPigeon TeamFebruary 12, 20269 min read

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.

TL;DR

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:

  1. Connect — Your app opens a TCP connection to an SMTP server
  2. Handshake — The server responds with a greeting. Your app sends EHLO to identify itself
  3. TLS — If using STARTTLS (port 587), encryption is negotiated
  4. Authenticate — Your app sends credentials (username + password or API key)
  5. Envelope — Your app specifies the sender (MAIL FROM) and recipient (RCPT TO)
  6. Data — Your app sends the email content (headers, body, attachments)
  7. Queue — The SMTP server accepts the message and queues it for delivery
  8. Delivery — The server looks up the recipient's MX records and forwards the message
  9. 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

PortProtocolUse Case
587STARTTLSRecommended. Starts unencrypted, upgrades to TLS. Standard for email submission.
465Implicit TLSConnection is encrypted from the start. Re-standardized in RFC 8314.
25Plaintext / STARTTLSServer-to-server relay only. Blocked by most ISPs and cloud providers (AWS, GCP, Azure).
2525STARTTLSUnofficial 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

SMTPEmail API
ProtocolTCP connection with SMTP commandsSingle HTTP POST request
AuthenticationUsername + passwordAPI key in header
ConnectionPersistent TCP connectionStateless HTTP request
ServerlessDoesn't work wellWorks perfectly
TrackingNot built-inOpen/click tracking included
Error handlingSMTP status codes, retry logicJSON responses, automatic retries
Setup complexityConnection pooling, timeouts, TLSOne 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:

ProviderSMTP HostPortNotes
SendPigeonsmtp.sendpigeon.dev587Included in all plans
SendGridsmtp.sendgrid.net587API key as password
Postmarksmtp.postmarkapp.com587Server token as password
Mailgunsmtp.mailgun.org587Domain-specific credentials
Amazon SESemail-smtp.{region}.amazonaws.com587IAM credentials
Gmailsmtp.gmail.com587App 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