On this page
Every email provider offers both, the documentation implies they are interchangeable, and so the decision usually gets made by whichever snippet appears first in the docs. That works until something fails, at which point the two are not remotely alike: one tells you what went wrong while your function is still running, and the other tells you tomorrow, by bouncing.
This is a comparison of what actually differs in practice: failure visibility, where each one can run, what happens under load, and which constraints decide it for you before preference gets a say.
What actually differs
SMTP is a protocol from 1982 designed for servers to hand mail to other servers over a long-lived connection. The client opens a socket, greets the server, negotiates TLS, authenticates, names the sender, names each recipient, uploads the message body and says goodbye. Every step has its own reply code and its own way to fail.
An HTTP API collapses that into one request. You post JSON, you get a status code and a body, and the function that made the call has the answer before it returns. That is the whole difference, and almost every practical consequence follows from it.
Failure visibility is the real gap
When an API rejects a malformed address, you get a 400 and a message naming the field. The request that caused it is still on the stack, the user is still waiting, and you can show them something useful.
When SMTP accepts a message, the usual reply is 250 Ok: queued. That means the relay has taken responsibility for the message, not that anyone will receive it. If the address does not exist, you find out when an asynchronous bounce arrives at the envelope sender, minutes or hours later, in a mailbox your application is probably not reading.
| What went wrong | Over the API | Over SMTP |
|---|---|---|
| Malformed address | 400 with the field named, immediately | Often accepted, then bounced later |
| Recipient unsubscribed | Rejected or skipped, with a reason | Depends entirely on the relay |
| Credential revoked | 401 on the next call | 535 at AUTH, after the connection is open |
| Over the rate limit | 429 with the limit stated | 421 or a dropped connection |
| Provider degraded | 5xx you can retry deliberately | A timeout somewhere in the handshake |
Where each one can actually run
This is the constraint that usually ends the discussion. Modern deployment targets are built around HTTP and restrict raw network access:
- Supabase Edge Functions, Cloudflare Workers, Deno Deploy: no outbound TCP to arbitrary ports. SMTP is simply unavailable, whatever library you install. Sending from an Edge Function is an HTTPS call or nothing.
- Vercel and similar serverless platforms: TCP is possible on some runtimes, but connection reuse is not, so every invocation pays the full handshake and TLS negotiation.
- Containers and traditional servers: both work. SMTP connection pooling is viable here, and this is the only place it genuinely helps.
- Corporate networks: outbound 587 is frequently blocked, while 443 never is.
The handshake cost matters more than it looks. On a long-lived server you open one connection and push hundreds of messages through it, and the per-message overhead disappears. In a serverless function that handles one request and freezes, you pay for the socket, TLS and authentication on every single email.
When SMTP is the right answer
SMTP is not legacy baggage to be eliminated. It is the only universal interface email has, and that universality is worth something specific: it is the answer when you do not control the sending software.
- Software you cannot modify. An ERP, a warehouse system, a scanner, a monitoring appliance. They take a host, a port and a password, and nothing else.
- Platforms with an SMTP field and no HTTP client. Most WordPress mail plugins, many self-hosted tools, a lot of internal scripts.
- Moving providers without a code change. Swapping SMTP credentials is a configuration change; swapping an API is a deploy.
- One long-running sender pushing high volume, where a pooled connection removes per-message overhead entirely.
The honest summary: if a human is writing the integration and the environment allows HTTPS, take the API. If the sender is a box with a settings screen, give it SMTP credentials and make sure something is reading the bounces.
What you give up by choosing SMTP
An API is not only a different transport; it is a different amount of information. SMTP hands over a finished message, so the relay knows the envelope and nothing else.
- Structured contacts. The API knows who the recipient is as a record, with fields, list membership and engagement history. SMTP knows an address.
- Suppression at the moment of sending. An API can refuse to mail someone who unsubscribed last week. A relay given a finished message will usually just send it.
- Triggering content you did not write. Calling an automation means the copy lives in the platform rather than in your repository, which is what stops email templates from becoming code you maintain.
- Per-message status. An id you can look up beats an inbox full of delivery status notifications.
None of that matters for a monitoring alert going to one internal address. All of it matters the moment the recipients are customers, because that is when consent, suppression and reporting stop being optional.
A decision you can make in a minute
| Your situation | Use |
|---|---|
| Edge or serverless runtime | API. TCP is not available to you. |
| You are writing the integration yourself | API. Errors arrive where you can handle them. |
| Sender is software you cannot change | SMTP. It is the only interface it has. |
| You want to swap providers by changing config | SMTP, accepting the loss of per-send detail. |
| High volume from one long-running process | Either. Pooled SMTP removes the overhead that usually argues against it. |
| Recipients are customers rather than your own team | API, so suppression and consent are checked before sending. |
Most products end up using both: the API from application code, and SMTP credentials issued to the two or three systems that only speak SMTP. That is a reasonable end state rather than a failure to standardise, as long as both paths share one suppression list. If they do not, an unsubscribe recorded through one is invisible to the other, and you will eventually mail someone who asked you not to.
For the implementation details either way, sending from Next.js covers keeping credentials server-side, and why emails go to spam covers the authentication records that matter whichever transport you pick.
Frequently asked questions
Is an email API faster than SMTP?
Per message from a short-lived process, yes, because SMTP pays for a socket, a TLS negotiation and an authentication exchange before the message moves, while an API call is one request. From a long-running server with a pooled connection the difference largely disappears, since the handshake is amortised across many messages.
Can I send email over SMTP from a serverless function?
Sometimes, and not at all from edge runtimes. Supabase Edge Functions, Cloudflare Workers and Deno Deploy do not allow outbound TCP to arbitrary ports, so SMTP is unavailable no matter which library you reach for. Traditional serverless platforms may allow it but cannot reuse connections, so every invocation pays the full handshake.
Does SMTP hurt deliverability compared with an API?
Not in itself, since both hand the message to the same sending infrastructure and the same authentication applies. The indirect risk is bounce handling: an API reports failures inline, while SMTP returns them asynchronously to the envelope sender, and a team that never reads that mailbox keeps mailing dead addresses until their reputation suffers.
When should I still use SMTP?
When the sender is software you cannot modify, such as an ERP, a scanner, a monitoring appliance or a WordPress plugin that offers a host, port and password and nothing else. It is also reasonable when you want to switch providers with a configuration change rather than a deploy.
Can I use both an API and SMTP at the same time?
Yes, and most products end up doing exactly that: the API from application code, SMTP credentials for the few systems that only speak SMTP. The requirement is that both paths consult the same suppression list, otherwise an unsubscribe recorded through one route is invisible to the other.
