Skip to Content
sesameSend

Rate limits and quotas

Per-team API rate limit and send quotas, and the variables that tune them.

API rate limit

Every team gets 10 requests per second, shared by all its keys. Set OPENSEND_RATE_LIMIT_PER_SEC to change it, or 0 to turn it off. The count lives in Postgres, so it holds across serverless instances.

Every response carries IETF headers:

ratelimit-policy: 10;w=1
ratelimit-limit: 10
ratelimit-remaining: 7
ratelimit-reset: 1

Past the limit you get 429 rate_limit_exceeded with a retry-after header (seconds). Back off for that long, then retry. If the limit check itself errors, the request is allowed.

Send quotas

VariableDefaultOn breach
OPENSEND_MONTHLY_EMAIL_LIMIT50000429 monthly_quota_exceeded
OPENSEND_DAILY_EMAIL_LIMITnone (no daily cap)429 daily_quota_exceeded

These are your own guardrails. They do not replace the SES quota. GET /usage shows what the team has used.

A team can also have its own daily_email_limit in the teams table. It overrides the variable. The daily and monthly windows are UTC calendar periods, and a scheduled email counts against the day it goes out, not the day you created it.

Automatic pause

To protect your SES reputation, a team is paused when its real sends reach either threshold over the window (24 hours by default):

  • Bounces: 5% permanent bounces, with at least 100 recipients in the window.
  • Complaints: 0.1% complaints, with at least 1000 recipients in the window.

The rate is events divided by recipients (to, cc and bcc) of the same set of emails. That set is the emails SES accepted whose send time falls in the window, so a scheduled email counts on the day it goes out. Simulator traffic (resend.dev) does not count.

VariableDefault
OPENSEND_AUTO_PAUSEon; set 0 to turn it off
OPENSEND_BOUNCE_PAUSE_RATE0.05
OPENSEND_COMPLAINT_PAUSE_RATE0.001
OPENSEND_GUARD_WINDOW_HOURS24
OPENSEND_GUARD_MIN_SENDS100
OPENSEND_GUARD_COMPLAINT_MIN_SENDS1000

What a paused team sees

  • Every send path (single, batch, broadcasts, automations, inbox replies) returns 403 sending_paused, and the message includes the reason.
  • If the guard tables cannot be read, sends fail closed with 503 guardrail_unavailable. Retry shortly. It never reads as "not paused".
  • A broadcast or scheduled email that was already queued is held, not failed. It goes out after you unpause. Broadcasts are also held, then resumed, when they hit the daily or monthly quota.

Resume a team with:

pnpm exec tsx --env-file=.env scripts/unpause-team.mts <team_id>

Scheduled times

A scheduled_at in the past means send now. It must be no more than 30 days ahead, or the request returns 422.

Broadcast speed

BROADCAST_SEND_RATE is emails per second for broadcasts. The default is 1. See Broadcasts.

Idempotency

Retrying a send with the same Idempotency-Key returns the stored response with idempotent-replayed: true instead of sending twice. Keys last 24 hours.