Rate limits & idempotency

Request limits, send caps, per-project throttling, and safe retries.

Request rate limits

Management endpoints — templates, automations, segments, topics, senders, domains, metrics and settings — are rate-limited per API key, in a rolling 60-second window:

KindLimit
Reads (GET)120 / minute
Writes (POST, PATCH, PUT, DELETE)30 / minute

Responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Policy. Exceeding a limit returns 429 with code rate_limited and a retry_after_ms field telling you exactly how long to wait.

These are request limits, separate from the send limits below. Sending itself is governed by the per-project throttle and the daily/monthly caps rather than by this per-key request window, so don't rely on a 429 here to pace your sending — use the caps.

Limits are enforced per running instance, so a deployment behind several instances may allow proportionally more in aggregate. Treat them as a runaway-loop backstop, not a quota to pace against.

Send caps

Separately from request limits, every send is checked against a daily and a monthly cap. Both count your own messages in the current UTC calendar day / month — so the daily window resets at 00:00 UTC and the monthly one on the first of the month.

The ceiling comes from your provider plan (Resend or SendGrid), resolved from what the provider reports plus its published plan limits. On top of that, a project can set an optional daily_send_cap that only ever tightens the daily limit further — the tightest applicable limit wins:

CapCeilingTightened byError
DailyProvider plan's daily limitdaily_send_cap project setting429 daily_cap
MonthlyProvider plan's monthly limit— (no operator override)429 monthly_cap

0 means unlimited for either cap. Leaving daily_send_cap unset means "rely on the provider plan" rather than imposing a separate ceiling.

Caps are a hard stop enforced inside the send path, so they apply to automations and scheduled sends too — not just direct API calls. Read the current values and today's usage with GET /api/v1/settings.

Throttling

Sends are throttled per project (best effort, ~8/second). When a burst exceeds the rate, the extra sends don't fail — they fall back to the worker queue and return 202 with status queued. You don't need to implement client-side pacing for normal traffic.

The kill switch

A project has a sendsPaused kill switch. While it's on, every send returns 409 with code sends_paused. It's re-checked on the worker path too, so queued and scheduled sends respect it as well.

Idempotency

Pass an Idempotency-Key header on POST /api/v1/emails to make retries safe:

curl https://sendandretain.com/api/v1/emails \
  -H "Authorization: Bearer aem_your_key" \
  -H "Idempotency-Key: welcome-jane-1" \
  -H "Content-Type: application/json" \
  -d '{ "to": "[email protected]", "template": "welcome" }'

The key is unique per project (≤256 characters) and is also forwarded to the provider. A repeat with the same key returns the original result with deduplicated: true instead of sending again:

{ "id": "msg_3aF9c1", "status": "sent", "deduplicated": true }

Events support the same idea with dedupe_key on POST /api/v1/events.

If a request fails with 500/internal or times out, retry with the same Idempotency-Key. Retrying without one risks sending twice.

On this page