Skip to content

Rate Limits & Quotas ​

Sends are throttled per tenant and per queue. Transactional and broadcast traffic are metered separately, so a broadcast blast cannot exhaust your transactional budget.

Design for throttling ​

  • Batch within a single request. Each send endpoint accepts an array in data (currently up to 100 items) — prefer one call with many items over many calls with one item.
  • Back off on 429. Treat a rate-limit response as transient: retry with exponential backoff and jitter, capped at ~5 attempts.
  • Make retries idempotent. Reuse the same transaction_uuid on retry so a duplicate never sends twice.
ts
async function sendWithRetry(body: unknown, attempt = 0): Promise<Response> {
  const res = await fetch(`${ME_BASE}/sms/send_messages/message`, {
    method: "POST",
    headers: {
      "X-Token": ME_TOKEN,
      "X-TenantID": ME_TENANT,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (res.status === 429 && attempt < 5) {
    const wait = Math.min(2 ** attempt * 250, 8000) + Math.random() * 250;
    await new Promise((r) => setTimeout(r, wait));
    return sendWithRetry(body, attempt + 1);
  }
  return res;
}

Don't tight-loop retries

Retrying immediately on 429 makes throttling worse. Always wait, and always add jitter so many clients don't retry in lockstep.

Scheduling smooths spikes ​

For large sends with a future target time, set digest and let the platform pace delivery rather than firing everything at once. Cancel before dispatch with the broadcast batch status route, GET /sms/broadcast/{batchId}.