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_uuidon 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}.
