Guides

Rate limits

Generous by design: a real integration, even a sync job paging through every contact, should never meet them. They exist to stop a runaway loop.

Per key

Each key has its own allowance, so one busy integration never slows another down. Allowances refill continuously.

REST APIBursts of 300 requests, refilling at 600 a minute
Agent inbox APIBursts of 120 requests, refilling at 120 a minute, per agent key

Over the allowance, a request is answered 429 rate_limited with a retry-after header: the whole number of seconds to wait.

429 Too Many Requests
HTTP/1.1 429 Too Many Requests
retry-after: 2
content-type: application/json

{
  "error": "rate_limited",
  "message": "Too many requests for this key. Try again in 2 seconds."
}

Account limits on sending

Sending has limits of its own, set by the account's plan and checked however many keys you use. They protect your sending reputation as much as ours.

Transactional email, per hourTrial 100, Starter 2,000, Business 10,000
Transactional email to one address20 a day
Campaigns and automationsOne shared hourly allowance by plan
Test copies of a campaign30 an hour, to members of the account only
An AI agentThe daily limit you set on the agent

An account limit is answered 429 too_many, and the message says which limit. A transactional message that was refused does not count towards either transactional limit.

Retrying

Wait for the seconds in retry-after, then try again; do not retry in a tight loop. For a send, keep the same idempotencyKey on every attempt so the message goes once.

Node
async function call(url, init, attempt = 0) {
  const res = await fetch(url, init);
  if (res.status === 429 && attempt < 5) {
    const seconds = Number(res.headers.get('retry-after') ?? 1);
    await new Promise((r) => setTimeout(r, seconds * 1000));
    return call(url, init, attempt + 1);
  }
  return res;
}

Need more

Planning a large import or a launch day? Talk to us before it happens and we will look at the account's limits with you.