API Reference
Rate limits
The per-route limits, and what to do when you hit one.
Limits are per API key, per minute, and vary by route.
| Route | Per minute |
|---|---|
POST /v1/subscribe | 30 |
POST /v1/contacts | 120 |
GET endpoints | 120 |
POST/DELETE on list members | 60 |
POST/DELETE on suppressions | 120 |
GET /v1/reports/* | 30 |
Subscribe is the tightest because it is the one route a browser can reach, and an unlimited public endpoint that sends email is an invitation to use your form to mail a stranger repeatedly.
Reports are lower than other GET routes because each
one adds up figures across your whole workspace rather than fetching a
single record. Send back the ETag as If-None-Match and an unchanged
report answers 304 Not Modified, which is cheap enough that 30 a
minute comfortably covers a dashboard refreshing every few seconds.
Requests count against the limit whether they return 200 or 304.
When you hit one
{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Retry after 24 seconds."
}
}
429, with a Retry-After header in seconds. Wait that long, then
retry.
Backing off properly
async function call(url, options, attempt = 0) {
const res = await fetch(url, options);
if (res.status !== 429 || attempt >= 5) return res;
const after = Number(res.headers.get('Retry-After') ?? 1);
// Jitter, so a fleet of workers that hit the limit together does
// not come back together and hit it again.
const wait = after * 1000 + Math.random() * 1000;
await new Promise((r) => setTimeout(r, wait));
return call(url, options, attempt + 1);
}
Importing a large audience
For a bulk import, prefer the dashboard’s CSV import. It is built for volume and gives you a report of what merged, what was skipped and why.
If you must do it over the API, 120 contacts a minute is about two a second. Space the calls rather than firing them in parallel and retrying the failures, which just converts one limit into a slower version of the same limit.
Confirmation emails have their own limit
Separately from the route limit, we will not send a second confirmation email to the same address within a short window, however many times the form is submitted.
Your call still succeeds and still returns confirmation_sent. We
simply do not mail them again. A signup box that emails on every
submission is a way to mail a stranger repeatedly by pressing a
button.
Updated at, Saturday, October 10, 2026