# emails.sh vs Resend

Resend set the standard for developer email. emails.sh matches the API shape, adds inbound receiving and replying, and an install path built for agents.

Also searched as: Resend alternative, Resend vs emails.sh, transactional email API.

## What Resend is

Resend is the product that reset expectations for developer email. A single POST creates a send, the React Email library lets you write templates as components, domain verification is a short screen rather than a support thread, and the dashboard shows you what happened to a message without a ticket. Its free tier is 3,000 emails a month capped at 100 a day, and paid plans move in monthly volume bands. If you have used it, you already know the shape of our API, because Resend is the shape we are copying and we would rather say that than pretend otherwise.

## Where Resend is the better choice

Pick Resend if you want the safest choice available today. It has years of production volume behind it, a large community writing about it, an integration in nearly every framework starter, and React Email is genuinely the nicest way to write HTML email if your stack is React. We are the new entrant. If your decision criterion is "how many other companies are betting on this", the honest answer points at them.

## Where emails.sh differs

Two things differ, and only two are worth your attention. First, receiving: we run inbound MX, parse the MIME, and thread replies against the message that started them, so a customer answering your notification lands somewhere you can read and reply from. Second, the integration path is written for the assistant doing the work: every page on this site is available as markdown at the same URL plus .md, there is an /llms.txt, and there is a skill you install into Cursor or Claude Code so it knows the whole API without fetching a doc.

### Replies come back

Inbound is a first-class part of the product: MX records on your domain, parsed MIME, attachments extracted, and threading across In-Reply-To and References. You can send a message and read the answer through the same API, which matters the moment your product wants a conversation rather than a notification.

### Built for an assistant to integrate

Add /llms.txt to your assistant, or install the skill, and it writes the send call, the domain verification step, and the webhook handler in one turn. Errors are prose an agent can act on: a 422 tells it the domain is unverified and gives it the URL to verify at, rather than returning a code it has to look up.

### Nine language clients, not four

Node and TypeScript, Python, PHP, Ruby, Go, Rust, Java, .NET, and Elixir, all with the same method names and the same error shapes. If your backend is Elixir or Rust, you get a maintained client rather than a community wrapper or a hand-rolled HTTP call.

## You do not have to rewrite anything

emails.sh serves a Resend-compatible API that speaks their exact request format, their exact response format, and their exact error shape. Their official SDK talks to it without noticing. Their Node client resolves its host from RESEND_BASE_URL before falling back to api.resend.com, so for a JavaScript codebase the entire migration is two environment variables and a deploy. The resend package stays in your package.json, your imports stay, and resend.emails.send() returns the same object with the same id.

```bash
RESEND_BASE_URL=https://api.emails.sh/resend
RESEND_API_KEY=esh_live_yourkey
```

Node, PHP, Ruby, Go, and Rust read RESEND_BASE_URL. Python reads RESEND_API_URL instead, which is the one detail that quietly leaves a migration still pointing at Resend. .NET and Elixir take a configuration value rather than a variable, and Java hardcodes its host so it needs our client instead. Their contact, segment, audience, broadcast, topic, template, webhook, and suppression endpoints are all served on the compatibility layer too, in their field names and their list envelopes, so an integration that does more than send keeps working. Roll the whole thing back by removing the two variables.

Full chapter: https://emails.sh/docs/migrate-resend

## Moving from Resend

The compatibility endpoint handles the code. These are the parts of an account that are not code, and `npx @emails.sh/cli migrate resend` does the first three for you: it reads your domains, webhooks, and suppression list from Resend, prints a plan, and waits for a yes.

1. **Point the SDK here, or swap it** Set RESEND_BASE_URL and RESEND_API_KEY and nothing else changes. If you would rather move properly, install @emails.sh/sdk: the send call takes the same from, to, subject, html, text, reply_to, cc, bcc, headers, attachments, and tags fields, with scheduled_at spelled send_at.
2. **Re-verify the domain** Domain verification does not transfer between providers. Add the domain here, publish our DKIM and return-path records alongside the Resend ones, and widen your SPF record to include both while they coexist. Nothing breaks: a domain can carry several DKIM selectors at once.
3. **Point webhooks at your handler** Our events are email.delivered, email.bounced, and email.complained, signed with x-emailssh-signature. Verify the signature against your signing secret, then map the payload onto whatever your Resend handler already does.
4. **Cut over by percentage** Send 5% here for a week and compare bounce and complaint rates against the Resend numbers on the same traffic. Then 50%, then all of it. You are not warming anything: reputation follows the domain, and the domain is not moving.

## Side by side (checked July 2026)

| | emails.sh | Resend |
| --- | --- | --- |
| Pricing shape | Per email sent, in monthly volume bands. Domains, API keys, and seats are not billed separately. | Per email in monthly volume bands, with a free tier under it. |
| Free tier | 3,000 emails a month, 100 a day, no card, sending from your own verified domain. | 3,000 emails a month, capped at 100 a day. |
| Sending domains | One screen: add the domain, copy SPF, DKIM, and return-path records, watch them go green. Unlimited domains on every plan. | One screen, DNS records checked live. Multiple domains supported. |
| Deliverability posture | Shared pools separated by send type, bounce and complaint tripwires below the thresholds that get senders suspended, dedicated IP on request. | Strong track record, shared pools, dedicated IPs on higher plans. |
| Receiving mail | Inbound MX, parsed MIME, and In-Reply-To threading, so a reply arrives as part of a conversation you can query rather than as a raw webhook body. | Limited. Sending is the product and receiving is not the story. |
| Logs and retention | Every message searchable in the dashboard and readable at GET /v1/emails/:id, with its delivery, bounce, and complaint events attached. | A good dashboard with per-message events, retained by plan. |
| Templates | Send html and text in the request, or store a template and reference it by id. No visual builder, because the markup belongs in your repo next to the code that sends it. | React Email, which is the best-in-class option if you write React. |
| SDKs and frameworks | Nine clients (node and typescript, python, php, ruby, go, rust, java, .net, elixir) plus curl, and framework guides from Next.js to Laravel, Rails, and Django. | Node, Python, PHP, Ruby, Go, Java, .NET, Rust, plus wide framework coverage. |
| Marketing and lifecycle | Audiences, segments, scheduled broadcasts, and automations, on the same verified domains and the same delivery log as your transactional mail, and on a reputation pool that is kept separate from it by force. | Audiences with contacts, segments, and topics, plus Broadcasts and Automations with delay, wait-for-event, and condition steps. Their CLI can create an automation from a JSON file, and all of it except automations is reachable through the Resend-compatible surface as well. |
| Webhooks | email.delivered, email.bounced, and email.complained, each signed with x-emailssh-signature so you can verify the body before you act on it. | Signed delivery, bounce, complaint, open, and click events. |
| Path for a coding agent | Every page served as markdown at the same path plus .md, an /llms.txt index of the whole site, and a Cursor and Claude Code skill you install once. | Excellent human docs. No installable assistant skill. |
| Support | Email support on every plan including the free one, answered by the people who wrote the API. We are small, and that cuts both ways. | Email support, with priority tiers on higher plans. |

## Questions

### Is emails.sh a Resend clone?

The API shape is deliberately close, because a developer moving between them should not have to relearn a request body. What differs: inbound email that is parsed and threaded rather than a webhook body, nine language clients, an SMTP relay on the same quota, every page served as machine-readable markdown with an installable assistant skill behind it, and automations that are a YAML file you can review in a pull request rather than a canvas in a dashboard.

### Does Resend do inbound email?

Not as a headline feature. Resend is built around sending, and its own positioning is about the send path. If receiving and replying is central to what you are building, that is the clearest reason to look here.

### Which has better deliverability?

Resend has more years of sending behind it, and we will not claim to beat that from a standing start. Deliverability is mostly a property of your list, your content, and your domain authentication, all three of which move with you. Send 5% of your real traffic through both for a week and read your own numbers.

### Can I run both at once?

Yes, and you should during a migration. A domain can publish several DKIM selectors, so both providers can sign for it while you compare. Split by percentage in your own code and move the dial when the numbers agree.

### Can I keep using the resend npm package?

Yes. We serve a Resend-compatible API at https://api.emails.sh/resend that speaks their wire format exactly, errors included, so their SDK cannot tell the difference. Set RESEND_BASE_URL to that URL and RESEND_API_KEY to a key starting with esh_, and no other line of your code changes. The full table of which SDK in which language takes which variable is at https://emails.sh/docs/migrate-resend.

### What does the compatibility endpoint not support?

Four things, and they are small. Their contact-properties API declares fields ahead of time, and we have no such registry: an attribute here is created by being written. Their OAuth flow has no equivalent, because we authenticate with API keys only. Their API request log does not exist, though the log of the mail those calls produced does. And their events API is folded into the message: GET /emails/:id carries the whole delivery timeline rather than one word. Everything else is served: sending, batch sending, scheduling and rescheduling, domains, API keys, contacts, segments, audiences, broadcasts, topics, templates, webhooks, and the suppression list. Each remaining refusal answers with a message naming what to use instead, never a generic failure.

All comparisons: https://emails.sh/vs
Docs: https://emails.sh/docs.md
Pricing: https://emails.sh/pricing.md