What is an email API? How sending email from your code works

Learn how an email API works, from the API key and HTTP request to delivery events, retries and rate limits, with curl and Node.js examples.

9 min read
On this page

Your product sends email whether you planned it or not. A signup needs a confirmation, a payment needs a receipt, a forgotten password needs a reset link. Somewhere in your code there is a line that has to say "send this email to this person, now", and an email API is what that line talks to.

This guide explains what an email API is, how a send travels from your code to an inbox, and the parts that separate a working integration from a fragile one: delivery events, idempotency, rate limits and domain authentication. The examples are real requests you can adapt.

What is an email API?

An email API is a web service that sends email on behalf of your application. Your code makes an HTTPS request: who the email is for, what it says, and an API key that proves the request comes from you. The provider takes it from there. It builds the message, signs it for your domain, hands it to the recipient's mail server, retries when that server is busy, and tells you what happened.

You could run your own mail server instead. Few products do, because the hard part of email is not sending, it is being trusted, and an email API rents you that work.

Email API vs SMTP relay vs marketing platform

There are three common ways for a product to send email, and they solve different problems.

Three ways to send email: an SMTP relay speaks the mail protocol from any language, an email API takes HTTP and JSON and sends events back, a marketing app is where people build campaigns by hand
Code sends through an API or SMTP. People send through an app.
SMTP relayEmail APIMarketing platform
Who sendsYour code or any mail-capable softwareYour codeA person in a dashboard, or an automation
HowThe SMTP protocol, usually on port 587An HTTPS request with JSONCampaign editor, lists, segments
ErrorsNumeric SMTP codes, sometimes lateStructured JSON, at onceShown in reports
Events backRarely, or through a separate systemWebhooks or a status endpointBuilt into reports
Best forLegacy apps, devices, tools that only speak SMTPReceipts, resets, notificationsNewsletters, launches, nurture sequences

SMTP relay is the original way, and almost everything supports it. But errors are terse and nothing tells you later that the email bounced. An email API wraps the same delivery in one HTTP call that answers with a message id and pushes events back. A marketing platform is for people, not code. Most SaaS products end up using an API for the email their code triggers and a marketing tool for everything else. The line between the two is the subject of transactional vs marketing email.

How an email API works

How an email API works: your app sends an HTTPS POST with its API key, the email API checks and queues the email, then delivers it to the recipient mail server. Delivered or bounced comes back to your app as a webhook event
One request out, events back as delivery happens.
  1. 1
    Your code authenticates. Every request carries your API key, usually in an Authorization: Bearer header. The key identifies your account and decides what the request is allowed to do.
  2. 2
    It sends an HTTP request. A POST with a JSON body: the recipient, a subject and HTML, or a template id with variables to fill in.
  3. 3
    The API checks and queues it. It validates the fields, refuses addresses known to bounce, renders the template, and puts the email in a queue. You get a message id back within moments.
  4. 4
    The provider delivers it. It signs the email with DKIM for your domain and connects to the recipient's mail server. If that server says "try later", the provider retries for you.
  5. 5
    Events come back. Delivered, bounced, opened, clicked or marked as spam: each one arrives later, as a webhook or through a status endpoint.

The important detail is in step 3. A successful response does not mean the email arrived. It means the provider accepted it, or that it left. Whether the recipient's server took it is only known seconds or minutes later, which is why events matter.

Email API examples: curl and Node.js

The examples below use Fromly's transactional endpoint, which works like most email APIs. The shape is the same elsewhere: a URL, a bearer key, a JSON body.

curl -X POST https://api.fromly.io/v1/transactional \
  -H "Authorization: Bearer $FROMLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "ada@example.com",
    "emailId": "<template id>",
    "data": { "amount": "12 EUR", "orderId": "4521" },
    "idempotencyKey": "order-4521"
  }'

The answer comes straight back:

{ "messageId": "…", "status": "sent", "to": "ada@example.com", "replay": false }

The same call from Node.js with fetch, which is built into Node 18 and later, plus the error handling a real integration needs:

async function sendReceipt(order) {
  const res = await fetch('https://api.fromly.io/v1/transactional', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.FROMLY_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      to: order.email,
      emailId: process.env.RECEIPT_TEMPLATE_ID,
      data: { amount: order.total, orderId: order.id },
      // Names the event, not the attempt: a retry cannot send twice.
      idempotencyKey: `order-${order.id}`,
    }),
  })

  if (res.status === 429) {
    const seconds = Number(res.headers.get('Retry-After') || 1)
    throw Object.assign(new Error('rate limited'), { retryAfter: seconds })
  }
  if (!res.ok) {
    const body = await res.json().catch(() => ({}))
    throw new Error(`send failed (${res.status}): ${body.error}`)
  }
  return res.json() // { messageId, status, to, replay }
}

Store the messageId next to the order. It is how you will match the events that arrive later.

Templates and variables

HTML inside your code means a deploy for every changed sentence. Most APIs let you design the email once as a template and pass only the values that change. In the examples above, emailId points to a template and data fills it:

Hi {{contact.firstName|there}}, you paid {{data.amount}} for order {{data.orderId}}.

The text after | is a fallback for when a value is missing, so nobody receives "Hi ,". Good template engines also escape the values, so a name typed as HTML by a user can never inject markup into your email.

Delivery events and webhooks

Events are how you learn what became of an email after the send call returned. Most providers report the same set:

EventWhat it meansWhat to do with it
deliveredThe recipient's mail server accepted the emailNothing, or mark the notification as sent
bouncedIt could not be deliveredOn a hard bounce, stop sending to that address
complainedThe recipient marked it as spamStop sending anything but what they strictly need
openedThe email was openedTreat as a hint: some mail apps load images in advance
clickedA link was followedA reliable sign of attention
failedIt could not be sent at allLog the error and alert someone

There are two ways to receive them. Webhooks push each event to a URL on your server as it happens. Verify their signature, answer quickly with a 2xx and process the event in the background. Providers retry failed webhooks, so the same event can arrive twice. The other way is polling: you ask for the status of a message by its id, for example GET https://api.fromly.io/v1/messages/<messageId>. It is simpler to build and fine for a support screen.

Bounces and complaints are the events you cannot ignore. Sending again to an address that hard bounced is how a bounce rate climbs and a sender reputation erodes.

Retries, idempotency and rate limits

Networks fail in an annoying way: a request can time out after the email has already left. If your code retries blindly, the customer gets two receipts. If it does not retry, a real failure goes unnoticed.

Idempotency key: the first request with key order-4521 times out, the retry uses the same key, and the API recognizes it and sends nothing new, so the customer gets one receipt
The same key on a retry means the same email, sent once.

An idempotency key solves this. You send a value that names the event, such as order-4521 or reset-42-2026-10-07. If a second request arrives with the same key, the API returns the original result instead of sending again. In the example API, the reply says so with "replay": true.

Rate limits cap how many requests a key can make in a period. Past the limit, the API answers 429 Too Many Requests, usually with a Retry-After header saying how many seconds to wait. Respect it, and add exponential backoff with a little randomness for other temporary errors (5xx). Do not retry 4xx errors other than 429: a missing field or a revoked key will fail the same way every time.

If your code can produce a burst, send from a queue with controlled concurrency rather than a loop that fires all at once.

Domain authentication for an email API

Before an email API can send for yourapp.com, you have to prove you own it. The provider gives you DNS records to add:

  • SPF, which lists the servers allowed to send for your domain. See what is SPF.
  • DKIM, a public key that lets inboxes verify the signature on each email. See what is DKIM.
  • DMARC, which ties both to the From address people see and sets a policy. See what is DMARC.

Since February 2024, Gmail and Yahoo require all three from bulk senders, and unauthenticated email from anyone is far more likely to land in spam. Check your domain here once the records are in place:

Try it: Deliverability Setup CheckerOpen the full tool

We read its SPF, DKIM and DMARC records from public DNS and tell you what to fix.

While you are in your DNS, plan two subdomains: one for transactional email (notify.yourapp.com) and one for marketing (news.yourapp.com). Each builds its own reputation, so a campaign that draws complaints never delays a password reset.

How to choose an email API provider

Most providers send email reliably. The differences show up in the details you only meet in production.

CheckWhy it matters
Domain authentication guided step by stepYou will set up SPF, DKIM and DMARC, and later debug them
Idempotency keysSafe retries without duplicate receipts
Events by webhook or status endpointYou need to know about bounces and complaints
Automatic suppression of bounced and complained addressesProtects your reputation when your code forgets
Clear rate limits and Retry-AfterPredictable behavior under load
Scoped API keysA leaked key should not be able to do everything
Separate transactional and marketing streamsOne cannot damage the other
Pricing at your expected volumeSome plans jump sharply past a threshold

Before committing, send test emails to Gmail and Outlook and confirm SPF, DKIM and DMARC pass in the original headers.

Email API security: keeping keys safe

An API key is a password that can send email as your company. Treat it that way.

  • Keep it server-side. Never call an email API from the browser or ship the key in a mobile app. Anyone can read it there.
  • Store it in an environment variable or a secrets manager, never in your repository. If one was ever committed, revoke it, even if the commit was deleted.
  • Use scoped keys. A key that can only send transactional email cannot be used to blast your whole list if it leaks.
  • One key per environment. Revoke one without breaking the others.
  • Rotate regularly. Create the new key, deploy it, then revoke the old one.

Where to go next

An email API is the plumbing; what travels through it still has to be wanted and sent to addresses that exist. Read transactional vs marketing email to decide what belongs on which stream, the email deliverability guide for what decides the inbox, and check any address before you send with the free email checker.

FAQ

Frequently asked questions

What is an email API?
An email API is a web service that sends email for your application. Your code makes an HTTPS request with an API key, the recipient and the content, and the provider handles delivery, retries, bounces and tracking.
Is an email API better than SMTP?
For most applications, yes. An API answers with structured errors and a message id, supports idempotency keys and templates, and reports delivery events back to you. SMTP is still useful for legacy software, devices and tools that only know how to speak SMTP.
Do I need my own domain to use an email API?
Yes, for anything beyond testing. You send from your own domain and prove it with SPF, DKIM and DMARC records in your DNS. Without them, Gmail, Yahoo and Outlook are likely to filter or reject your email.
What is an idempotency key?
A value you send with a request that names the event, such as order-4521. If the same request arrives twice, the API recognizes the key and does not send a second email. It makes retries safe after a timeout.
How do I know whether an email was delivered?
The send call only tells you the email was accepted for sending. Delivery, bounces, opens, clicks and spam complaints arrive afterwards, through webhooks pushed to your server or a status endpoint you can query with the message id.
Can I call an email API from the browser?
No. Calling it from the browser exposes your API key to anyone who opens the developer tools. Call it from your server, a serverless function or a background job, and keep the key in an environment variable.
Can I send newsletters through an email API?
Technically yes, but marketing email needs unsubscribe links, consent tracking and list management that a sending API alone does not give you. Keep campaigns in a marketing tool or a separate marketing stream, apart from your transactional email.

Turn your signups into customers with email

Connect Supabase and Stripe, let your agent draft your first flow, and send your first 1,000 emails for free.

Start with 1,000 free emails