Sulat.com / Writing on AISaturday, November 14, 2026
Developer Tools/14 November 2026

Resend says its Go email API runs at roughly half the latency and a seventh of the memory

Resend says it rebuilt its email API in Go in seven weeks and 140 pull requests. At a 50% traffic split, the company reports a 31 ms median against 58 ms on Node, and about 180 MB of memory per task against 1.3 GB.

A matte-black 3D Resend R logo beside a server rack with a fast stream of cream envelopes flying out of it

At a glance

  • Resend says it rebuilt its email API in Go over seven weeks and roughly 140 pull requests, and that Go was handling all of its email API traffic by October.
  • At a 50% traffic split, Resend reports a POST /emails median of 31 ms on Go against 58 ms on Node, and memory per task of about 180 MB against about 1.3 GB.
  • Resend says Node tasks could stall while parsing large request bodies, including ones the rate limiter was rejecting, and that S3 timeouts reached customers as 408 and 524 errors.
  • Resend built a harness that sends identical requests to both services and compares responses, database rows, S3 objects, idempotency and rate limits, and says its Go HTML-to-text port matched all 60 Node fixtures.

Resend says its email API now runs entirely on Go, after a seven-week rewrite of the Node.js service that took roughly 140 pull requests, according to the company’s engineering post. With traffic split evenly between the two services, Resend reports that Go showed roughly half the median latency and about one-seventh the memory per task.

Zeno Rocha, Resend’s CEO, shared the post on X on 13 November. He wrote that as a Node.js developer he wasn’t on board at first, “then I saw the numbers.” Resend’s own account described the project as moving “our most critical API to Go.”

Why Resend moved off Node

Resend says the Node service had two failure modes. During bursts of large requests, a whole task could become unresponsive. One investigation traced that to parsing and serialising large request bodies, including requests the rate limiter was already rejecting. Both are CPU-heavy operations that run synchronously in Node, so they block the event loop, the single thread that runs Node’s code, until they finish.

Diagram: one big request sits at the front of Node's line while three requests behind it wait
One big request holds up everything queued behind it in Node.

The second problem was storage. Resend says S3 timeouts surfaced as customer-facing 408 and 524 errors. Those two problems set the requirements for the replacement: predictable behaviour under load, lower resource use, and control over how long each operation can take.

How Resend checked the two services agree

Resend built a harness that sends the same requests to Node and Go and compares what each does. It covers responses and errors, database rows, S3 objects, idempotency (the guarantee that repeating a request doesn’t send a second email) and rate limits. The company says checking the HTTP response alone would leave too much untested, because both services could return a success while saving different metadata or producing different email content.

Diagram: one request goes to both Node and Go, then five checks compare the results
Resend sends the same request to both services and compares five things.

The old service became the reference, and Resend kept some Node quirks on purpose, since changing unexpected behaviour can break an integration that depends on it. Its example is the conversion of HTML-only emails to plain text. The Go library Resend first picked produced reasonable text but differed from the Node library: elements marked data-skip-in-text="true" appeared in the output, and links could gain URL text. Resend wrote a Go port of the specific behaviour its API uses, and says the first version matched all 60 fixtures generated from the Node implementation.

Derich Pacheco, one of the two engineers interviewed in the post, says much of the API’s behaviour lived in Node and its npm packages rather than in Resend’s own code. His example is attachments: the Node SDK accepts a Buffer, which JSON.stringify turns into a JSON object, and Node turned it back into bytes with one call. Go has no Buffer, so the team rebuilt that conversion by hand, JavaScript’s coercion rules included.

What the rollout needed

The new service used fewer resources per request, but Resend says it still had to double task sizes twice during the rollout. It also added capacity ahead of a recurring top-of-hour traffic spike, introduced per-request timeouts for S3 uploads and tuned Redis connection pools as traffic grew. Fabio Kubo, the other engineer interviewed, says a widget showing the split of HTTP status codes gave the team a quick check that the new service behaved like the old one, because a random share of requests went to each.

Pacheco says the team deleted some of the Node code once the port was stable. Parts of the old service existed only to protect the event loop, and he describes the email-size check as having had an elaborate trick to avoid scanning large bodies byte by byte, which the team cut down to the one part that still saves CPU. He also says the batch endpoint went from the decision to move it to production traffic in about a week.

The numbers

Resend’s figures come from the 50% rollout, with half of requests on Node and half on Go, both on the same task size. It says Go used about 62% less CPU per request. Median latency for POST requests to /emails fell by roughly 50%, and p99, the latency 99% of requests come in under, fell by about 16%. Resend says much of the slow tail is time spent waiting on S3 and the database, so cutting it further means work on those dependencies.

Diagram: Node versus Go on median latency, memory per task, tasks at peak and CPU per request
Go posts lower numbers than Node on all four measures in Resend’s 50% rollout.
MetricNodeGo
POST /emails median latency58 ms31 ms
POST /emails p95 latency149 ms107 ms
POST /emails p99 latency498 ms420 ms
Batch median latency149 ms74 ms
Batch p99 latency1,721 ms1,363 ms
Tasks at peak3410
Memory per task~1.3 GB~180 MB
CPU per request (normalised)1.00x~0.38x
Source: Resend’s engineering post. Figures are from the 50% rollout.

Resend titles that section “The preliminary results” and gives no figures for all-Go traffic. On 10 November the company also made its own sending infrastructure, Forge, available to every customer.


Discover more from Sulat AI & Tech News

Subscribe to get the latest posts sent to your email.