SMTP vs. REST API: how to choose and when to switch
How SMTP and REST API email transports differ in practice (statefulness, serverless fit, templates, idempotency, error handling), when each is the right choice, and a step-by-step plan for migrating from SMTP to a REST API.

SMTP and a REST API are the two ways an application hands an email to a sending service. SMTP is the older, connection-oriented protocol that every mail system understands, while a REST API is a single stateless HTTP request. They differ in how they hold a connection, what features they expose beyond plain delivery, and how they report failure, and that difference decides which one fits modern infrastructure like serverless functions and containers.
How SMTP and a REST API differ
SMTP is a stateful, connection-oriented protocol. The application opens a TCP connection to the
mail server, performs a multi-step handshake (EHLO, MAIL FROM, RCPT TO, DATA), transmits the message, and then either keeps the
connection open for the next message or closes it. Reusing the connection reduces overhead across a
batch, but a single email from a short-lived process pays the full cost of that handshake every
time.
A REST API call is stateless. The application sends an HTTP POST request with a JSON body, receives a response, and that's the entire interaction. There's no connection to manage and no session state to clean up.
Why the difference matters in modern infrastructure
In serverless environments like Lambda or Cloud Functions, functions spin up on demand and are frozen or killed when idle. An SMTP connection established in one invocation is usually dead by the next, leaving two options: reconnect on every send (slow) or write connection pooling logic that fights the execution model (fragile). A stateless HTTP request avoids the problem entirely because there's nothing to keep alive between invocations.
Kubernetes and ECS face the same issue at different scales. Containers scale up and down, and an SMTP connection tied to a specific container becomes stale when that container is replaced. REST calls go through a load balancer and don't care which container they hit.
Some platforms, including Lettr, support both protocols, which allows REST from a Node.js Lambda function and SMTP from a legacy CRM that can't be modified, with both flows feeding the same analytics and suppression list.
What the API gives you that SMTP can't
SMTP's job is to deliver a complete email message from one server to another. The protocol doesn't understand templates, metadata, scheduling, or structured errors, so anything beyond "here's a message, deliver it" becomes your problem to solve in application code.
A REST API accepts structured data, which enables two features that materially change the integration: server-side templates and idempotency.
1. Server-side templates
The request carries a template ID and a set of merge variables instead of the full HTML, and the email service renders the final message. The application code never touches the email layout, and non-developers can edit templates without a code deploy.
POST /emails
{
"to": "user@example.com",
"template_id": "order-confirmation",
"merge_data": {
"name": "Alex",
"order_id": "ORD-4821",
"items": [
{ "name": "Widget Pro", "qty": 2, "price": "$24.00" }
]
}
}The SMTP equivalent builds the HTML in application code before handing it to an SMTP library:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
msg = MIMEMultipart("alternative")
msg["From"] = "orders@yourapp.com"
msg["To"] = "user@example.com"
msg["Subject"] = "Your order ORD-4821"
# You built this HTML yourself, in your app code
html = render_template("order-confirmation.html", name="Alex", order_id="ORD-4821")
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP("smtp.provider.com", 587) as server:
server.starttls()
server.login("apikey", "your-api-key")
server.send_message(msg)Both approaches work. The REST version keeps email content on the email platform rather than tying it to your deployment cycle, so a copy change doesn't require a code change.
2. Idempotency
A timed-out network call leaves the outcome unknown: the server may or may not have processed the
request. With an idempotency key, retrying is safe: the server recognizes the key
from the first attempt and returns the original result instead of reprocessing. SMTP has no
equivalent. A connection that drops after DATA but before the 250 OK leaves the message in the same ambiguous state, and a naive retry can deliver it twice.
For password resets, order confirmations, and other transactional flows where duplicates are visible to the user, that ambiguity matters. The API solution requires a single additional request header.
Other features worth knowing about
Two smaller things the API provides that are awkward or impossible over SMTP:
- Structured metadata. Attach fields like
user_id,campaign_name, orenvironmentto a send, and they flow through to webhooks and analytics. The SMTP equivalent is customX-headers that the provider may or may not parse. - Scheduled sending. A
send_attimestamp tells the platform to queue the message until the requested time. No cron job required.
Error handling
How a transport reports failure decides how much error-handling code it needs, and this is where the two diverge most sharply. A failed API call returns a JSON response with a status code, an error type, and a readable message:
{
"status": 422,
"error": "invalid_recipient",
"message": "The address user@invalid-domain.test could not be validated. Check the domain exists and has MX records."
}Switch on the error type and route it to an alert channel. Same pattern as any other API in your codebase.
SMTP errors look like this:
550 5.1.1 The email account that you tried to reach does not exist.The first digit indicates the category (5xx is permanent, 4xx is temporary). The enhanced status
code (5.1.1) carries additional detail, defined in the RFC 3463 table. The text that follows
is freeform and varies by provider.
Handling SMTP errors properly means parsing these text responses, mapping provider-specific
messages to your own error categories, and handling the worst case: the connection drops
mid-handshake with no response, leaving it unknown whether the server accepted the message. With a
REST API, the result is unambiguous: either a 2xx with a message ID, or a failure.
When SMTP is the right choice
SMTP isn't always wrong. Many teams are right to stick with it, and the most common reason is that they don't actually own the sending code.
WordPress plugins, legacy CRMs, ERP systems, and network appliances that send alert emails
typically expose only an SMTP configuration screen. When the sending code can't be changed,
an SMTP relay is the only option. Lettr's relay (smtp.lettr.com) handles this case:
set the host, port (465 recommended), and an API key as the password. The existing system then
sends through Lettr without any code changes.
The other reason to stay on SMTP is that migration risk doesn't always justify the cost. A tool that's already configured, sending low volumes, and working well is rarely worth the cost of building a custom integration. The same goes for monitoring: if your alerting is built around SMTP logs, connection metrics, or mail queue depth, switching to a REST API means rebuilding that observability layer.
For anything new, the API is almost always the better starting point. When your stack spans both worlds, such as a WordPress site alongside a custom Node.js service, choosing a provider that supports both protocols keeps the analytics and suppression list unified. See how Lettr compares to other providers.
FAQ
Is a REST API faster than SMTP?
Can I use both SMTP and a REST API with the same provider?
When should I keep using SMTP instead of switching to an API?
What is an idempotency key and why does SMTP lack one?
DATA but before
the 250 OK leaves the outcome unknown, and a naive retry can deliver a duplicate.Bottom line
New code should use the API; existing SMTP integrations that work should be left alone until a change forces the move. The forcing changes are connection problems in a serverless or container environment and the cost of maintaining templates in application code. When one hits, migrate one email type at a time and verify delivery before the next.
The fundamentals of deliverability are independent of all of this. Proper domain authentication, bounce handling, and engagement monitoring are required whether you're using SMTP or REST. The protocol decision is about how your code communicates with the email service; what happens after the message leaves your application is the same in both cases.
For both transports from a single provider (a drop-in SMTP relay and a REST email API), create a free Lettr account and select the right one for each part of your stack.