What is an email API? How it works and examples
Email APIs send and receive email through structured HTTP requests instead of SMTP, with event tracking and idempotent retries. This guide walks through authentication, payload construction, response handling, webhook tracking, and the decision framework for choosing between REST APIs and SMTP.
On this page
TL;DR: An email API is a REST interface that sends and receives email through structured HTTP requests instead of raw SMTP handshakes. You authenticate with an API key, POST a JSON payload, get an immediate status code, and track delivery through event webhooks, which gives you idempotent retries and inbound routing that SMTP does not provide natively. Choose an API when you need event tracking, inbound parsing, or dedicated inboxes for AI agents, and choose SMTP for raw outbound delivery with minimal integration overhead.
Your transactional email works until it doesn't. The difference between a working email API integration and a production incident is whether you understand what happens after the 202 response. This article walks through the complete request lifecycle (authentication, payload, response codes, idempotency, webhooks), shows working HTTP and Next.js examples, and gives you the decision framework for choosing between REST and SMTP.
Anatomy of an email API request
An email API exchanges structured JSON over HTTPS for every operation, from sending a password reset to receiving an inbound reply. Unlike SMTP's multi-step conversation, the API model treats each request as an independent transaction with a clear beginning and end.
How email APIs process requests
The request-response model works like this: your application sends an HTTPS POST with a JSON body containing the message details, the server accepts or rejects the request immediately, then queues the message for async delivery. SMTP is a stateful, connection-oriented protocol where your code opens a socket to port 587, performs a handshake, and streams the message. A REST API call is stateless: you send the request, receive a response, and the interaction ends.
The provider validates your request against security policies, queues the message, and routes it through SMTP or similar transport behind the scenes. The provider fires event notifications to your webhook endpoint, not in the initial HTTP response. APIs support higher concurrency than SMTP because they're stateless HTTP calls rather than open socket connections.
Production triggers for email APIs
Production sends fall into five categories:
Trigger Type | Example Endpoint | Payload Requirements |
|---|---|---|
Password reset email | POST /emails (path varies by provider) | to, from, subject, html, text |
Welcome email | POST /emails (path varies by provider) | to, from, subject, template_id or html, variables (provider-dependent) |
Notification | POST /emails/batch (path varies by provider) | recipients or to array, subject, html (field names vary by provider) |
Verification code | POST /emails (path varies by provider) | to, from, subject, text or html (payload structure varies by provider) |
Agent reply | POST /emails (path varies by provider) | to, from, subject, in_reply_to, text or html (field names vary by provider) |
Endpoint paths and field names differ between providers, so treat these as representative patterns and confirm the exact schema in your provider's API reference.
Webhook systems typically cover major email lifecycle events: accepted, delivered, failed, opened, clicked, unsubscribed, complained, and stored, though exact event coverage varies by provider. That event granularity matters when you're debugging why a user didn't receive a password reset.
Email API vs SMTP: key differences
The architectural difference between REST APIs and SMTP determines what you can build and how much operational overhead you accept. SMTP is not obsolete, but it solves a narrower problem than most developers assume.
Aspect | Email API (REST) | SMTP |
|---|---|---|
Authentication | Scoped API key in header | Username/password |
Payload format | JSON over HTTPS | MIME over TCP socket |
Response handling | Immediate HTTP status code | Multi-line reply codes |
Event tracking | Webhooks for delivery and engagement events | None (requires polling or logs) |
Inbound support | Webhook with full message body | Requires separate MX infrastructure |
Retry behavior | Idempotency keys prevent duplicates | No native idempotency |
Idempotency and event tracking
Idempotent operations produce the same result when you retry them, without side effects. An idempotency key is a client-generated value, sent as a header on a request that has side effects, which the server uses to recognize a retry of an operation it has already performed.
SMTP has no native idempotency. If your connection drops after DATA but before you receive the server's final reply, you don't know if the message was accepted. Retrying creates a duplicate. Stripe's documentation states that it retains keys for at least 24 hours and can remove them after that, but retention policies change, so confirm the current terms and set your retention period longer than the longest retry delay any client, queue, or batch job can plausibly produce.
SMTP has no native event tracking. You send a message and hope it arrives. Email APIs fire webhooks for every lifecycle event: delivered, bounced, opened, clicked, complained. You subscribe an HTTPS endpoint, verify signatures, and process events asynchronously.
The mechanics of email API request lifecycles
An email API request typically moves through several stages, though the exact flow varies by provider. Understanding what happens at each stage, including failure modes, is the difference between a reliable integration and a 2 AM page.
1. Secure API key authentication
Most email APIs ask you to send your API key in the Authorization header formatted as a Bearer token, but the two are not the same thing and the scheme is not universal: an API key is a unique identifier tied to your account or project, while a Bearer token is a specific format used inside the Authorization header. Some providers accept the key in a custom header such as X-API-Key, a query parameter, or a cookie, so confirm the exact scheme in your provider's documentation. Secret keys belong in environment variables, and only your Data Access Layer should access process.env to keep secrets from being exposed to other parts of the application.
Security checklist:
- Store keys in environment variables, never in source code, client-side bundles, or other locations where they may be exposed or logged
- Use scoped keys with minimum required permissions (send-only, read-only, per-inbox)
- Rotate keys on a schedule and immediately after any suspected exposure
- Keep keys out of client-side code and public environment variables
- Return only what the client needs from Server Actions. Never return process.env values in responses
2. Building your email payload
The JSON payload contains everything the server needs to construct and route your message. Here's a minimal example:
{
"from": "noreply@yourdomain.com",
"to": "user@example.com",
"subject": "Reset your password",
"html": "<p>Click <a href='...'>here</a> to reset</p>",
"text": "Click here to reset: https://..."
}
Providers typically require from and to, and most also require subject unless you send with a template ID. You need at least one of html or text. Attachments, headers, and tags are optional but useful for tracking and debugging. The system receives a request that includes sender and recipient addresses, subject, message body, attachments, and any metadata.
3. Interpreting API response status codes
The HTTP status code tells you what happened and what to do next. When the client must change something about the request, common examples include 400 Bad Request, 401 Unauthorized, 404 Not Found, and 429 Too Many Requests. When the server failed to complete an otherwise valid request, examples include 500 Internal Server Error, 502 Bad Gateway, and 503 Service Unavailable.
Status Code | Meaning | Action |
|---|---|---|
202 Accepted | Request queued for delivery | Store message ID, wait for webhook |
400 Bad Request | Invalid payload (missing field, bad email) | Log error, fix payload, do not retry |
401 Unauthorized | Invalid or expired API key | Check key, rotate if needed, do not retry |
429 Too Many Requests | Rate limit exceeded | Back off, retry after delay specified in Retry-After header |
500 Internal Server Error | Server bug | Log, retry once, then fail gracefully |
503 Service Unavailable | Temporary outage or maintenance | Retry with backoff, monitor status page |
Failure mode runbook:
- 429 rate limiting: HTTP 429 signals that a client has exceeded the rate limit. The Retry-After header indicates how long to wait before retrying. Implement exponential backoff with jitter and strict retry caps.
- 5xx server errors: A 503 might resolve quickly or indicate a larger outage. A 500 suggests a bug. Differentiate: 429 means back off and retry, 503 means retry with backoff but monitor for extended outages, 500 means log and possibly retry once before failing.
- Circuit breaking: If you see sustained 5xx responses, stop sending for a cooldown period. Hammering a failing API makes incidents worse.
4. Preventing duplicates via idempotency
Idempotency keys are sent in the Idempotency-Key header. Use them for all POST requests. The key must be a UUIDv4 or similar high-entropy string, up to 255 characters. If the server generated it, a retry would arrive with a new key and the whole mechanism would be pointless.
What happens when you retry without one? You get duplicate sends. If your queue retries a failed request that actually succeeded, the user receives two password resets. Idempotency keys make retries safe.
5. Tracking delivery via event webhooks
When you POST a send request, the API returns 202 Accepted, not "delivered". Everything that matters after that point happens asynchronously, over seconds to days, and the provider reports it to you as a stream of events.
Webhook payloads include the event type, message ID, timestamp, and event-specific data (bounce reason, click URL, user agent). A common approach is around 8 attempts spread over roughly 24 hours using exponential backoff, after which the message goes to a dead-letter queue.
Webhook handling best practices:
- Verify signatures: Verify the signature using the provider's public key or shared secret
- Accept and queue: Accept the webhook, store the raw payload in a queue, and return a 200 response
- Process async: Process the payload asynchronously from the queue. Don't do heavy processing in the webhook handler itself
- Dead-letter queue: Route events that exhaust their retry schedule to a dead-letter queue so they stay available for inspection and replay instead of disappearing
send.io's inbound routing receives email at custom addresses, parses attachments, and routes the full message to your application logic in one webhook. Inbound payload shape varies by provider: some include the full body and attachments in the webhook, while others deliver metadata only and require a follow-up API call to fetch message content. Check the payload format before you commit, because a metadata-only webhook means an extra request per inbound message when you're parsing replies or verification codes.
Sample HTTP request for email delivery
Here's what a production-ready email API request looks like, from headers to error handling.
Required request headers and body
Field | Required | Description |
|---|---|---|
from | Yes | Sender email address (must be a verified domain) |
to | Yes | Recipient email address or array of addresses |
subject | Yes | Email subject line |
html | Conditional | HTML body. At least one of |
text | Conditional | Plain-text body. At least one of |
attachments | No | Array of |
headers | No | Custom headers as key-value pairs |
Example POST request payload
This curl command sends a password reset email:
curl -X POST https://api.example.com/v1/emails \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"from": "noreply@yourdomain.com",
"to": "user@example.com",
"subject": "Reset your password",
"html": "<p>Click <a href=\"https://...\">here</a> to reset</p>"
}'
Domain verification includes SPF, DKIM, and MX record setup. When you add the DNS records correctly, your domain often verifies within 15 minutes, but providers note the process can take up to 72 hours to complete. If your domain is still not verifying after 24 hours, Resend's troubleshooting guide recommends using a tool like dns.email to confirm your DNS records are publicly visible. DNS changes can take up to 72 hours to propagate globally, though they often complete much faster.
Handling API error responses
Error responses return JSON with an error code, message, and sometimes a field-level breakdown. Here's how to map common errors to fixes:
Error Code | Cause | Fix |
|---|---|---|
400 invalid_email | Recipient address malformed | Validate email format before sending |
400 missing_field | Required field absent (from, to) | Check payload against API schema |
401 invalid_key | API key wrong or revoked | Verify key, rotate if compromised |
429 rate_limit | Too many requests per second | Implement backoff, respect Retry-After |
500 server_error | Provider bug or outage | Log, retry once, check status page |
Email API example: JavaScript in Next.js
Next.js Server Actions give you a clean way to call email APIs without exposing keys to the client. Here's a production pattern.
Securing your email API Server Action
Server Actions access environment variables safely because they run server-side, not in the browser. By default, environment variables are only available on the Server. Next.js exposes any environment variable prefixed with NEXT_PUBLIC_ to the client, so never put credentials there.
'use server'
import { revalidatePath } from 'next/cache'
export async function sendPasswordReset(email: string) {
const apiKey = process.env.SENDIO_API_KEY
if (!apiKey) throw new Error('API key not configured')
const response = await fetch('https://api.send.io/v1/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
from: 'noreply@yourdomain.com',
to: email,
subject: 'Reset your password',
html: '<p>Reset link here</p>'
})
})
if (!response.ok) {
const error = await response.json()
throw new Error(`Send failed (${response.status}): ${error.message || JSON.stringify(error)}`)
}
return { success: true }
}
Managing email API status codes
Here's a Server Action with retry logic for 429 and 5xx responses:
'use server'
// In production, pull maxRetries from environment config
async function sendWithRetry(payload: any, maxRetries = 3) {
const apiKey = process.env.SENDIO_API_KEY
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch('https://api.send.io/v1/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': payload.idempotencyKey
},
body: JSON.stringify(payload)
})
if (response.status === 202) {
return { success: true, messageId: (await response.json()).id }
}
if (response.status === 429 || response.status >= 500) {
const delay = Math.min(1000 * Math.pow(2, attempt), 8000)
await new Promise(resolve => setTimeout(resolve, delay))
continue
}
// 400-level error, don't retry
const error = await response.json()
throw new Error(`Send failed: ${error.message}`)
}
throw new Error('Max retries exceeded')
}
Add jitter (random variance) to prevent thundering herd if multiple clients retry simultaneously.
When to use an API instead of SMTP
SMTP is not a legacy protocol you should abandon. It's a delivery mechanism that solves a narrower problem than most developers assume, and knowing the difference saves you from building the wrong integration.
REST API use cases for developers
Choose a REST API when you need structured event tracking, inbound routing, or agent inboxes: dedicated mailboxes with their own scoped credentials that AI agents use to send and receive email without a human account in the loop. APIs return message IDs and support webhook events that make correlation straightforward. When an AI agent sends an email as part of a multi-step workflow (signing up for a service, then waiting for a verification email), it needs to correlate the sent message with the received response. With SMTP, you're matching on email addresses and timestamps, which breaks down fast when multiple agents use the same mailbox.
Migration checklist (3 steps):
- Verify domain and update DNS records. Add SPF, DKIM, and MX records for the new provider. Most providers require domain verification for production use, including SPF, DKIM, and DMARC setup.
- Update API calls and test in sandbox. Map your existing send logic to the new API's payload format. Major providers share similar patterns (API key authentication, JSON payloads, async sending), so swapping one for another typically means updating the SDK import and field names rather than rewriting your templates.
- Run regression tests on all email flows. Send test emails for every trigger (password reset, welcome, notification) and verify webhooks fire correctly before cutting over production traffic.
For AI agents, scoped API keys per inbox let each agent operate with least-privilege credentials. Agents can bind their state machine to webhook events (wait for bounce or reply to decide next action).
When to choose SMTP over email APIs
SMTP remains the right choice for raw outbound delivery with minimal integration overhead. Most frameworks and languages include SMTP support out of the box or through mature libraries, which is why it remains the connectivity method of choice for systems ranging from new microservices to mainframe applications running for decades.
Older applications and platforms often rely on SMTP as their default email protocol, making it the easiest choice for integration without major rewrites. Getting started with SMTP is straightforward: you need SMTP credentials (host, port, username, password), and you're ready to send.
The trade-off: at most providers the SMTP endpoint handles delivery, while templating, event streaming, and security features are exposed through the REST API. Teams that route through SMTP may not have access to those capabilities without a separate API integration. For an ERP or a legacy platform, SMTP is the only option that does not require touching the application at all.
Evaluating email API providers
Provider evaluation comes down to three questions: does it handle bounces correctly, can you test before committing, and does webhook behavior match your reliability requirements?
Analyzing bounce and delivery rates
Bounce handling separates production-ready providers from toys. Start with the basics: SPF, DKIM, and DMARC records verified on your sending domain. If you configure a custom MAIL FROM domain (the subdomain used in the Return-Path address that receives bounce notifications, such as mail.yourdomain.com), it needs its own MX and SPF records so bounces route back correctly and DMARC alignment holds. A provider's dashboard should tell you plainly whether your domain is verified and sending-eligible, rather than leaving you to guess from bounce rates days later.
Suppression lists prevent you from sending to addresses that bounced, unsubscribed, or complained. When transactional and broadcast sends run on separate platforms, suppression state drifts, so a contact who unsubscribed from a campaign can keep receiving transactional mail until the two systems sync. Providers that keep both send types on one contact data model avoid that drift because an unsubscribe applies everywhere at once.
Analyzing onboarding and sandbox access
A provider without a sandbox forces you to test against production, which means real emails hitting real inboxes during development. Look for a sandbox mode that accepts API calls but doesn't deliver mail, test webhook endpoints that let you inspect payloads, and scoped test API keys separate from production.
Configuring webhook retry policies
Webhook reliability determines whether you lose events. Retries can't run forever. After the schedule is exhausted, the event should go to a dead-letter queue rather than being dropped, so it can be inspected and replayed later.
Use exponential backoff with a ceiling and jitter, e.g. 30s, 1m, 2m, 4m, 8m, 16m, 32m capped at 1 hour over ~7 attempts. When attempts are exhausted, the delivery must not simply vanish. A delivery that quietly stops being retried with no record is an event you have lost without noticing. Dead-lettering turns "lost" into "parked, visible, and recoverable by replay".
Signature verification prevents spoofed webhooks. Verify the HMAC signature in the webhook header against the raw request body using your webhook secret before processing the payload.
Verify that your provider's webhook format matches your requirements before migrating production traffic. Test inbound routing behavior in the sandbox before committing to an integration: provision a test inbox, send a message, and inspect the webhook payload to see exactly what your application receives.
FAQs
What is the difference between an email API and SMTP?
An email API is a REST interface that exchanges JSON over HTTPS with API key authentication, while SMTP is a stateful protocol that requires a multi-step handshake over a TCP connection. APIs provide structured event tracking via webhooks, idempotent retries, and inbound routing. SMTP does not.
Do email APIs require a dedicated IP address?
No. Most email APIs use shared IP pools by default. Dedicated IPs matter when you send high volume (100K+ messages per month is a common threshold) and need full control over sender reputation, but they require warmup and ongoing management.
How to handle rate limits with email APIs
Implement exponential backoff with jitter when you receive a 429 status code. Read the Retry-After header for the delay value, pause that long, then retry with an idempotency key so you don't create duplicates.
Can an email API handle both transactional and marketing emails?
Yes, if the provider supports both. send.io handles transactional sends, inbound routing, broadcasts, and contact management under one API. When transactional and broadcast contacts share one suppression list, an unsubscribe from a campaign applies to transactional sends without requiring a manual sync between two separate systems.
What happens when an email API request fails?
The API returns an HTTP status code that tells you what went wrong. 400-level errors mean your request is invalid (fix and don't retry), 429 means rate limited (back off and retry), and 5xx means server error (retry with backoff, then fail gracefully). Use idempotency keys to make retries safe.
Key terms glossary
API key: A unique identifier generated by an API provider and assigned to a client application to authenticate requests. API keys are typically long-lived tokens that combine identity and authorization into a single credential. Some providers support scoped keys restricted to specific permissions (send-only, read-only) and rotation independent of account passwords, but granular scoping is not universal. Confirm what your provider supports before relying on permission boundaries.
Webhook: An HTTP callback that delivers event notifications to your endpoint. Email APIs use webhooks to report delivery, bounces, opens, clicks, and inbound messages.
Idempotency: A property where retrying the same operation produces the same result without side effects. Idempotency keys prevent duplicate email sends when retrying failed requests.
SMTP: Simple Mail Transfer Protocol, the standard for email transmission since 1982. Requires a stateful connection and multi-step handshake to deliver messages.
MX record: A DNS record that specifies the mail server responsible for receiving email for a domain. Required for inbound email routing.
Inbound routing: The process of receiving email at custom addresses and delivering parsed messages to application logic via webhooks.
Scoped API key: An API key restricted to specific permissions or resources (per-inbox, send-only). Limits blast radius if the key is compromised.
Suppression list: A list of email addresses that should not receive sends (bounced, unsubscribed, complained). Prevents reputation damage and legal issues.
DKIM: DomainKeys Identified Mail, an authentication standard that cryptographically signs email to verify sender identity and prevent tampering.
SPF: Sender Policy Framework, a DNS record that specifies which IP addresses can send email for a domain. Prevents spoofing.
DMARC: Domain-based Message Authentication, Reporting and Conformance, a policy layer on top of SPF and DKIM that tells receivers how to handle authentication failures.