Skip to content
Sendozi

Reliability

Limits, large sends and pagination

Send endpoints allow 300 requests a minute per workspace, and one request can carry up to 1,000,000 recipients. A send is accepted into a queue and handed to the networks at a steady rate, so a large campaign takes time to go out. Lists use cursor pagination.

By SendoziUpdated 3 min read

The limits

WhatLimit
POST /v1/sms/send and /v1/sms/bulk300 requests a minute per workspace, counted together
Recipients in one send request1,000,000
Request body32 MB
Webhook endpoint create, update and delete20 a hour per workspace
Transactional sends per Sender IDThe per-minute and per-day limits on its profile
Idempotency-Key255 characters

The send limit counts requests, not recipients: a request with 50,000 recipients is one request. Send a campaign as one request (or a few), not one request per number.

Over a limit, the request returns 429 rate_limit_exceeded with a Retry-After header and retry_after_seconds in the error. Wait that long, then retry with the same Idempotency-Key.

Large sends

  1. 1

    Accepted

    Every number is validated, duplicates and opted-out numbers are removed, the full cost is reserved from the wallet, and each recipient is queued. Then the request returns 202 with the batch_id. Nothing has been sent yet.

  2. 2

    Sent

    Sendozi's workers hand queued recipients to the networks at a steady, platform-wide rate. Transactional messages are always sent first. Several batches sending at once share the rate, so a small send is not stuck behind a large one. If the network asks Sendozi to slow down, or stops answering, sending pauses for 15 seconds to 2 minutes and the waiting recipients go out when it resumes, rather than failing.

  3. 3

    Reported

    Delivery reports arrive as the networks return them, as message.delivered and message.failed webhooks. batch.completed arrives once every recipient has been sent, failed or skipped.

  • A large campaign is sent over minutes or hours, not instantly. Track it with GET /v1/message-batches/{batch_id}, whose progress counts move as it goes.
  • Live promotional SMS goes out 08:30 to 19:30 WAT. A promotional batch still queued when the window closes pauses and continues when it next opens. Schedule big promotional sends early in the day.
  • The request that queues hundreds of thousands of recipients takes longer to answer, because every recipient is checked and queued before the 202. Set a client timeout of several minutes for such a request, and always send an Idempotency-Key so a timed-out request can be retried without queuing it twice.
  • A campaign can also be split across several requests, each with its own Idempotency-Key. Each becomes its own batch.
  • You can stop a batch at any time with POST /v1/message-batches/{batch_id}/cancel. The unsent part of the reserved cost is returned to the wallet.
A 200,000-recipient campaign
Node.js
import { readFile } from "node:fs/promises";

const numbers = (await readFile("audience.txt", "utf8")).split(/\r?\n/).filter(Boolean);

const response = await fetch("https://api.sendozi.com/v1/sms/bulk", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SENDOZI_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "campaign-2026-09-24-october-offer",
  },
  body: JSON.stringify({
    sender: "YourBrand",
    route: "promotional",
    recipients: numbers,
    message: "October offer: 20% off everything until Sunday. Opt out: acme.ng/stop",
  }),
  signal: AbortSignal.timeout(5 * 60_000), // queuing 200k recipients takes a while
});

const body = await response.json();
if (!body.success) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data.batch_id, body.data.accepted_count, body.data.reserved_cost_kobo);

Backing off

  • Honour Retry-After on 429. Without it, back off exponentially with jitter, so a fleet of workers does not retry in step.
  • Reuse the same Idempotency-Key and the same request body on every retry.
  • Cap the attempts, and do not retry 4xx errors other than 429: they will repeat until the request is fixed.

Pagination

List endpoints return { data, has_more, next_cursor } inside the envelope. Pass next_cursor back as cursor until has_more is false. For messages and batches, limit defaults to 50 and is capped at 100; for a batch's recipients, 100 and 500.

Every message, page by page
cURL
curl "https://api.sendozi.com/v1/messages?limit=100" \
  -H "Authorization: Bearer $SENDOZI_API_KEY"

# then, with the next_cursor from that page:
curl "https://api.sendozi.com/v1/messages?limit=100&cursor=2026-09-24T09:14:22.117Z" \
  -H "Authorization: Bearer $SENDOZI_API_KEY"
Node.js
export async function* allMessages() {
  let cursor;
  do {
    const url = new URL("https://api.sendozi.com/v1/messages");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.SENDOZI_API_KEY}` },
    });
    const body = await response.json();
    if (!body.success) throw new Error(body.error.code);
    yield* body.data.data;
    cursor = body.data.has_more ? body.data.next_cursor : undefined;
  } while (cursor);
}
  • Results are newest first for messages and batches, and in queue order for a batch's recipients.
  • Use next_cursor exactly as returned. Pages stay stable while new messages arrive.
  • Sender IDs, webhook endpoints and API keys are returned as one list, without pagination.

Frequently asked questions

What is the Sendozi API rate limit?
300 send requests a minute per workspace, across /v1/sms/send and /v1/sms/bulk together. It counts requests, not recipients.
How many recipients can one Sendozi API request carry?
Up to 1,000,000, within a 32 MB request body. Each request becomes one batch.
How long does a send to 1,000,000 numbers take to go out?
It is accepted in one request and then sent at Sendozi's steady platform rate, shared with other traffic, so it goes out over hours rather than seconds. Live promotional sends go out only between 08:30 and 19:30 WAT. Follow progress on the batch.
What should I do when I get 429 rate_limit_exceeded?
Wait for the number of seconds in the Retry-After header, then retry with the same Idempotency-Key and body.

Track a large send

Read a batch's progress and every recipient's outcome.