MepMail Docs

Rate limits

The request, creation and sending limits a MepMail deployment enforces — and how to work within them.

MepMail enforces limits at four layers: HTTP request rate, credential protection, resource creation, and sending volume.

HTTP request rate

Two fixed one-minute windows apply to every call made with an API key, and a 429 rate_limit_exceeded answers whichever one is full:

WindowDefaultSet by
Per API key600 requests/minuteAPI_RATE_LIMIT_PER_MINUTE
Per team, across every key3,000 requests/minuteDeployment
  • Minting more keys does not multiply the cap — the team window spans them all.
  • A fixed window resets on the minute, so a burst that fills one window and another that fills the next can pass twice the allowance inside a couple of seconds. Pace the client instead of filling the window.
  • A 429 carries Retry-After (whole seconds left in the window): wait that long, or back off exponentially, before trying again.
  • Responses do not carry RateLimit-* headers: today the 429 and its Retry-After are how a client learns it went over.
  • Calls made through the hosted MCP server are bucketed per signed-in user instead — 600 calls/minute, the same setting — because an OAuth grant has no API key to count against.

What to do on a 429

  1. Wait. Honour Retry-After when the response has it, otherwise back off exponentially with jitter: 1s, 2s, 4s, 8s… A client that retries immediately just refills the window and stays refused.
  2. Stop retrying other 4xx. Only 429 and 409 concurrent_idempotent_requests are worth retrying; the rest of the 4xx range is deterministic (Errors).
  3. Make the retry safe. POST /emails and POST /emails/batch accept an Idempotency-Key: reuse the same one so a retry after a timeout or a 429 cannot deliver twice.
  4. Reduce the traffic. Batch up to 100 sends per call (POST /emails/batch) and up to 1,000 contacts per call (POST /contacts/batch), and serialize bursts instead of fanning out.
  5. Raise the ceiling if the workload is genuinely above it: the per-key window is API_RATE_LIMIT_PER_MINUTE on a self-hosted deployment. On Cloud, contact support — raising the team window is an operator decision.

Credential protection

  • More than 20 failed authentication attempts per minute from one IP answers 429 ("Too many failed authentication attempts", with Retry-After). This exists to make brute force pointless — stop retrying wrong credentials and check the key instead.
  • Invalid keys, revoked keys and keys used outside their permission level are 401/403, never silently accepted (Errors).

Resource creation

  • 10 domains per hour per team. Creating a sending identity provisions a shared AWS SES resource, so the cap keeps one team from exhausting the account's quota. It resets hourly. The plan's domain count is a separate limit and still applies.
  • 10 MCP OAuth client registrations per 15 minutes per address. A client that needs to register again should reuse its existing registration — re-registering on every launch is what trips this.

Sending volume

PlanIncludedPeriodPast the cap
Free100UTC daySends park as queued_quota and drain after the midnight rollover; once the parked backlog reaches 3× the daily cap, new sends are refused with 429 daily_quota_exceeded
Starter1,500UTC daySame parking behaviour and the same 3× backlog refusal
Pro / Scalepurchased volumemonthOverage billed per 1,000 when enabled; otherwise 429 monthly_quota_exceeded
  • Parking is deliberate: an over-quota daily send is accepted and queued — the API answers 200 with an id — so a burst near midnight is not lost. Read the email's status to tell a parked send from a queued one.
  • Monthly plans with overage enabled still hard-stop at 5× the included volume: a runaway integration (or a stolen key) can never produce an open-ended bill.
  • The per-second send rate is bounded by the deployment's SES quota — 14/s by default, following the account's real SES rate as it scales. Broadcasts are paced to leave transactional headroom (a 30% reserve by default).
  • Sending can stop before the quota does: a team whose hard-bounce or complaint rate crosses the guardrail gets 403 sending_paused, and broadcast sending waits on the platform's regional rate (403 broadcasts_paused). Both are in Errors.

Message shape limits

  • 50 recipients per email (to + cc + bcc combined).
  • 100 emails per batch call; an over-cap array is a 422.
  • Attachments (summed across the message, measured after decoding): 1 MB on Free and Starter, 5 MB on Pro, 10 MB on Scale.

Limits by plan (objects)

  • Contacts: 1,000 on Free, 10,000 on Starter, unlimited from Pro up.
  • Sender domains: 1 / 3 / 10 / unlimited (Free / Starter / Pro / Scale).
  • Teams per user: 1 / 2 / 5 / 10.

See Billing for prices and the full ladder.

MCP

  • 600 calls per minute per signed-in user on the hosted MCP endpoint (API_RATE_LIMIT_PER_MINUTE), then 429 rate_limit_exceeded. Calls are not charged against the team's key window.
  • The local @mepmail/mcp package talks to the REST API with an API key, so there the per-key and per-team windows above apply.
  • Dynamic client registration for MCP OAuth is rate-limited per address (10 per 15 minutes); a client that needs to register again should reuse its existing registration.

On this page