Sending

Send bulk SMS with POST /v1/batches

POST/v1/batches

Send to up to 10,000 numbers in one request. The whole cost is taken from the balance at once.

Request body

  • toarray of stringsoptional

    Numbers for one shared text. Use either to with text, or messages.

  • textstringoptional

    The shared text, required with to.

  • messagesarray of objectsoptional

    One {to, text} per recipient, for personal texts.

  • namestringoptional

    A label for the console, up to 100 characters.

  • referencestringoptional

    Your own id, up to 100 characters, returned on the batch and every message.

  • send_atstringoptional

    ISO 8601 time, 2 minutes to 45 days ahead.

curl https://bulksmsapi.cc/v1/batches \
  -H "Authorization: Bearer $BULKSMSAPI_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: spring-sale-0412" \
  -d '{
    "name": "Spring sale",
    "text": "Spring sale at Northwind: 20% off until Sunday. Opt out: northwind.example/sms",
    "to": [
      "+14155550123",
      "+447400123456",
      "+4915123456789"
    ]
  }'
202 AcceptedJSON
{
  "id": "bat_6TQ2W8M1ZK4N",
  "object": "batch",
  "name": "Spring sale",
  "status": "queued",
  "source": "api",
  "encoding": "GSM-7",
  "recipients": 3,
  "max_segments": 1,
  "cost": "0.02741",
  "refunded": "0.00000",
  "currency": "USD",
  "counts": { "scheduled": 0, "queued": 3, "sent": 0, "delivered": 0, "failed": 0, "canceled": 0 },
  "send_at": null,
  "reference": null,
  "created_at": "2026-11-07T09:31:40.102Z",
  "completed_at": null,
  "set_aside": []
}

Numbers that are set aside

Every number is cleaned and priced on its own. Numbers that cannot be sent are left out of the batch, not charged, and listed in set_aside with a reason:

reasonMeaning
invalid_numberNot readable as + and 7 to 15 digits after cleaning.
duplicateThe same number appeared earlier in the request.
unsupported_countryThe number does not belong to a priced country.
too_longIts personal text needs more than 10 segments.
empty_textIts personal text is empty.
suppressedThe number is on your do-not-send list.

If no number is left, the request fails with the code of the first reason (for example invalid_number) and nothing is charged.

Personal text per number

Use messages to give each recipient their own text: names, times, amounts. Each text is counted and priced on its own, so a batch can mix one-segment and two-segment messages.

curl https://bulksmsapi.cc/v1/batches \
  -H "Authorization: Bearer $BULKSMSAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Appointment reminders, 14 May",
    "send_at": "2026-05-13T16:00:00Z",
    "messages": [
      { "to": "+14155550123", "text": "Hi Ana, your dental check-up is tomorrow at 09:30." },
      { "to": "+14155550178", "text": "Hi Sam, your dental check-up is tomorrow at 11:00." }
    ]
  }'

Scheduling

With send_at the batch and its messages wait in status scheduled and move to the queue at that minute. The cost is taken when the request is accepted, so a scheduled batch always has its funds. A scheduled batch can be canceled until it starts.

POST/v1/batches/{id}/cancel

Cancels a scheduled batch: every message becomes canceled and the full cost returns to the balance as one refund line. A batch that has started answers 409 not_cancelable.

curl -X POST https://bulksmsapi.cc/v1/batches/bat_6TQ2W8M1ZK4N/cancel \
  -H "Authorization: Bearer $BULKSMSAPI_KEY"

Retrieve and list batches

GET/v1/batches/{id}

The batch with live counts per status. Poll it for progress, or use webhooks.

GET/v1/batches

Batches of the account, newest first, with limit (1 to 100) and starting_after (a bat_ id).

Batch fields

  • idstring

    bat_ followed by 12 characters.

  • statusstring

    scheduled, queued, sending, completed or canceled.

  • sourcestring

    api or console.

  • encodingstring

    GSM-7, or UCS-2 when any text needs Unicode.

  • recipientsinteger

    Numbers accepted into the batch.

  • max_segmentsinteger

    Largest segment count among the texts.

  • coststring

    Total taken from the balance, USD.

  • refundedstring

    Total returned so far for failed or canceled messages.

  • countsobject

    Messages per status: scheduled, queued, sent, delivered, failed, canceled.

  • send_atstring or null

    Scheduled time, if any.

  • completed_atstring or null

    When the last message reached a final status.

Larger lists

Split lists over 10,000 numbers into several requests, each with its own Idempotency-Key (for example the list id and the part number). Messages are handed to the network in order at up to 1,200 per minute per account, so a 50,000-number campaign is on its way in under an hour. The console takes 50,000 numbers per campaign from a paste or CSV file.

Send bulk SMS with POST /v1/batches | BulkSMSAPI Docs