What an email API is
An email API is an HTTP interface for sending and managing email. You send a POST request with the recipient, subject and body; the provider runs the mail servers, delivers the message and reports delivery status back through webhooks.
SMTP
- You manage connections
- Manual error handling
- Limited delivery tracking
- More code to get right
- Ports 25, 587 or 465
Email API
- No connections to manage
- Structured error responses
- Tracking and analytics built in
- Simpler to implement
- Works anywhere HTTPS does
APIs suit transactional email (order confirmations, password resets), automated notifications and programmatic campaigns. If you are building software that sends email, an API is almost always the better choice.
How an email API works
Authenticate
Each request carries an API key, usually in the Authorization header, which identifies your account.Send the request
POST the recipient, subject, body and any options to the send endpoint as JSON.The API validates and queues
It checks the request, queues the email and responds straight away with an ID.The provider delivers
It handles the SMTP connection, retries and hand-off to the recipient’s mail server.You get events back
Webhooks report each status change (delivered, bounced, opened, clicked), and you can query the API for a message’s status.
Code examples
The same request to Plunk’s send endpoint in six languages. Only to, subject and body are required.
curl -X POST https://send-api.akmarketingseo.com/v1/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "ana@acme.com",
"subject": "Welcome to Acme",
"body": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"from": "hello@acme.com",
"name": "Acme"
}'// Node.js 18+ has fetch built in
const response = await fetch('https://send-api.akmarketingseo.com/v1/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.PLUNK_API_KEY}`,
},
body: JSON.stringify({
to: 'ana@acme.com',
subject: 'Welcome to Acme',
body: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
// Optional
from: 'hello@acme.com',
name: 'Acme',
reply: 'support@acme.com',
}),
});
const result = await response.json();
if (response.ok) {
console.log('Sent', result.data.emails[0].email);
} else {
console.error('Failed to send', response.status, result);
}import os
import requests
response = requests.post(
'https://send-api.akmarketingseo.com/v1/send',
headers={'Authorization': f"Bearer {os.environ['PLUNK_API_KEY']}"},
json={
'to': 'ana@acme.com',
'subject': 'Welcome to Acme',
'body': '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
'from': 'hello@acme.com',
'name': 'Acme',
},
timeout=10,
)
if response.ok:
print('Sent', response.json()['data']['emails'][0]['email'])
else:
print('Failed to send', response.status_code, response.text)<?php
$ch = curl_init('https://send-api.akmarketingseo.com/v1/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . getenv('PLUNK_API_KEY'),
],
CURLOPT_POSTFIELDS => json_encode([
'to' => 'ana@acme.com',
'subject' => 'Welcome to Acme',
'body' => '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
'from' => 'hello@acme.com',
'name' => 'Acme',
]),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo $status === 200 ? 'Sent' : "Failed to send: $response";require 'net/http'
require 'json'
uri = URI('https://send-api.akmarketingseo.com/v1/send')
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = "Bearer #{ENV['PLUNK_API_KEY']}"
request.body = {
to: 'ana@acme.com',
subject: 'Welcome to Acme',
body: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
from: 'hello@acme.com',
name: 'Acme'
}.to_json
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.is_a?(Net::HTTPSuccess) ? 'Sent' : "Failed to send: #{response.body}"package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, _ := json.Marshal(map[string]string{
"to": "ana@acme.com",
"subject": "Welcome to Acme",
"body": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"from": "hello@acme.com",
"name": "Acme",
})
req, _ := http.NewRequest("POST", "https://send-api.akmarketingseo.com/v1/send", bytes.NewBuffer(payload))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("PLUNK_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
fmt.Println("Status:", resp.Status)
}A successful send returns the email ID for each recipient:
{
"success": true,
"data": {
"emails": [
{ "contact": { "id": "…", "email": "ana@acme.com" }, "email": "…" }
],
"timestamp": "2025-12-20T09:41:02.000Z"
}
}What email APIs do
| Area | What to expect |
|---|---|
| Sending | Single or batch sends, HTML and plain text, attachments, CC and BCC, custom headers, templates, scheduled sending. |
| Tracking | Delivery status, opens, clicks, bounce detection, spam complaints, unsubscribes, real-time analytics. |
| Webhooks and events | Notifications for deliveries, bounces, spam complaints, unsubscribes, opens and clicks, plus custom events. |
| Management | Suppression lists, contacts, domain verification, templates, API keys, rate limits. |
ana@acme.com
Welcome to Acme
tom@northwind.io
Welcome to Acme
tom@northwind.io
Welcome to Acme
old@contoso.com
Welcome to Acme
mia@fabrikam.com
Welcome to Acme
Best practices
Handle errors by status code
| Status | Meaning | What to do |
|---|---|---|
| 200 | Success | Store the email ID. |
| 400–499 | Client error, such as a bad request or failed validation | Fix the request. Do not retry. |
| 429 | Rate limit exceeded | Back off and retry later. |
| 500–599 | Server error | Retry with exponential backoff. |
Keep API keys out of code
Store them in environment variables, use separate keys for development, staging and production, and rotate them regularly or immediately if one leaks.Use webhooks, not polling
Webhooks deliver status changes in real time with far fewer requests.Respect rate limits
Queue emails, send in batches and back off exponentially on a 429.Validate addresses first
Check the format and common typos before calling the API, or verify deliverability with a tool like the email verifier.Use templates
Keep templates in your email provider instead of hardcoding HTML, so non-developers can edit content without a deploy.Monitor deliverability
Track bounce rate, spam complaints and engagement, and alert on anomalies.Test before production
Use a sandbox or test mode to exercise errors, retries and webhook handling.Log requests and responses
Redact keys and personal data, but keep enough to debug failed sends.Set timeouts and send asynchronously
Use a 10 to 30 second timeout and queue sends so a user request never waits on the email API.
Production
Used 2m ago
Staging
Used 1h ago
Development
Used yesterday
Choosing an email API provider
Deliverability
If mail lands in spam, nothing else matters. Look for a strong deliverability record.Features
Templates, webhooks, analytics, scheduling, attachments. Some providers focus on transactional email, others on marketing.Pricing
Per-email charges, monthly tiers and overage fees. Price your expected volume and watch for jumps at higher tiers.Developer experience
Clear docs, SDKs in your language, useful error messages and responsive support.Scale and reliability
Can it handle your peak volume? What uptime does the SLA promise, and is there failover?Compliance and security
GDPR and CAN-SPAM compliance, plus certifications such as SOC 2 and ISO 27001.