Email API

One request. Email delivered.

A JSON REST API with no client library required, and typed SDKs for Node, Python, and Go when you want one. Verify a domain, create a key, and the first send is a single POST — handled end to end by mail servers we run ourselves inside the EU.

POST /emails200 OK38 ms
{ "from": "[email protected]", "to": "[email protected]", "subject": "Your order is confirmed", "html": "<p>Thanks!</p>", "tags": { "category": "order" }}
{ "id": "em_9f3c2a7b" }
REST APIPOST /emails
SMTP:465 · TLS
Broadcastto an audience
Authenticatekey or credentials
Verify domainDKIM · SPF · DMARC
Suppress & meterbounces · ceilings
Sign and relayEU MTA · TLS
deliveredbouncedopenedclicked
Webhooksigned · retried
Your POST enters at the REST API door. Everything downstream of it is the same path an SMTP submission or a broadcast takes.

The shape of a send

POST /emails takes a sender, one or more recipients, a subject, and a body. Everything else is optional: cc, bcc, reply_to, custom headers, attachments, and tags that follow the message through the delivery log and every webhook it fires.

node
import { Eusend } from '@eusend_dev/sdk'

const client = new Eusend(process.env.EUSEND_API_KEY)

const { data, error } = await client.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Your order is confirmed',
  html: '<p>Thanks for your order!</p>',
  tags: { category: 'order_confirmation' },
})

if (error) throw error
console.log(data.id) // em_...

The SDKs return a { data, error } pair rather than throwing, so a failed send is a value you branch on. Every error carries a stable machine-readable code DOMAIN_NOT_VERIFIED is a code you can handle, not a string you have to parse.

Retries that don’t double-send

Networks time out mid-request, and the safe thing to do is retry — but a retried send is a duplicate password reset in someone’s inbox. Pass an Idempotency-Key and the second call returns the first response instead of sending again.

idempotency
// Safe to retry: the second call returns the first response
// instead of sending a second copy.
await client.emails.send(payload, {
  idempotencyKey: `order-${order.id}-confirmation`,
})

Batch and scheduled sends

  • Batch. POST /emails/batch takes up to 100 independent messages in one request. Each one succeeds or fails on its own — a bad recipient in slot 40 doesn’t take the other 99 with it.
  • Scheduled. Pass scheduled_at to queue a send up to 30 days out, as an ISO timestamp or a phrase like "tomorrow at 9am". Domain, suppression, and limit checks all run at schedule time, and the message can be rescheduled or canceled until it goes.

Test keys, not a sandbox account

Every account gets eu_test_ keys alongside live ones. Sends made with a test key run the full pipeline — accepted, assigned an ID, recorded in your logs, and firing the complete webhook lifecycle — but are never handed to a receiving mail server. You can build and verify the whole integration, including bounce handling, before a single real message leaves.

Three official SDKs

Node, Python, and Go clients wrap the same API under the same names, so switching languages doesn’t mean relearning the surface. All three are fully typed and report their own version in the User-Agent, so a delivery problem can be traced back to a specific client release.

  • Node.js @eusend_dev/sdk, with React Email rendering built in.
  • Python eusend on PyPI.
  • Go — a module the proxy serves straight from the tagged repo.
Send your first email in under five minutes.

Add a domain, publish the DNS records we generate, create a key, and POST. The quick start walks the whole path.

Create a free account