Sign inGet Started

Create a batch of SMS messages

POST
/v1/sms/batches
const result = await bird.sms.sendBatch({
  messages: [
    {
      from: "+15557654321",
      to: "+15551111111",
      text: "Hi Alice!",
      category: "marketing",
    },
    {
      from: "+15557654321",
      to: "+15552222222",
      text: "Hi Bob!",
      category: "marketing",
    },
  ],
});
Response202
{
  "data": [
    {
      "id": "sms_01krdgeqcxet5s7t44vh8rt9mg",
      "direction": "outbound",
      "status": "scheduled",
      "to": "+15551234567",
      "from": "+15557654321",
      "text": "Your order has shipped and is on its way.",
      "category": "transactional",
      "requested_language": "pt-BR",
      "resolved_language": "pt-BR",
      "template_id": "smt_01krdgeqcxet5s7t44vh8rt9mg",
      "template_version_id": "smv_01krdgeqcxet5s7t44vh8rt9mg",
      "template_content_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "segments": {
        "count": 1,
        "encoding": "GSM_7BIT",
        "characters": 41
      },
      "cost": {
        "amount": "0.00990",
        "currency_code": "USD",
        "transaction_amount": "0.00790",
        "passthrough_amount": "0.00200"
      },
      "tags": [
        {
          "name": "category",
          "value": "welcome"
        }
      ],
      "options": {
        "smart_encoding": true
      },
      "carrier": "Verizon",
      "mcc_mnc": "311480",
      "last_error": {
        "code": "invalid_destination",
        "description": "Carrier filtered as spam"
      },
      "created_at": "2026-05-21T12:00:00Z"
    }
  ],
  "summary": {
    "accepted_count": 2
  }
}

Sends up to 100 independent SMS messages in one request. Each item is a complete send request with its own recipient, content, ID, status, and cost. For a single message, use Send an SMS message instead.

Acceptance is all-or-nothing: every item is validated before any is queued, and one invalid item rejects the whole batch with a 422 (nothing is sent). A batch from a workspace with no wallet balance fails with a 402. The 202 response lists the accepted messages in submission order; each delivers asynchronously and is tracked individually, like a single send.

Request Payload

messages
array
required

SMS message send requests, up to 100. Each is an independent send; all are validated before any is queued.

Show child parameters
messages.to
string
required

Recipient phone number in E.164 format (for example +14155550100). One recipient per message. The number is stored and returned in canonical E.164; a recipient that cannot be routed returns a 422 SMSInvalidRecipient.

messages.from
string

Sender to send from. It must be a sender the workspace holds: a number it owns in E.164, such as +15557654321, a short code it holds, such as 24680, or an alphanumeric sender ID it has claimed, such as MyBrand. A sender the workspace does not hold returns a 422 SMSSenderNotConfigured, and an alphanumeric sender must also be permitted, and where required registered, for the destination country. Required on a free-text send and when sending a workspace template. Omitting it in either case returns 422. A built-in template selects its sender automatically and rejects from.

messages.text
string
required

Free-text message body. Required unless template is supplied (the two are mutually exclusive). At least 1 character, up to a 12-segment cap (roughly 1836 GSM-7 or 804 UCS-2 characters). Bird does not truncate; a body exceeding 12 segments is rejected with a 422. The cap applies to segments because GSM-7 and UCS-2 encodings differ in characters per segment.

messages.category
string

Content classification: why you are sending. Required on a free-text send; omit it on a template send, where the category is derived from the template. Where the destination country requires the sender to be registered, a category outside what that registration covers returns a 422 SenderCategoryNotPermitted.

Possible values: transactional, marketing, authentication, service

messages.validity_period
integer

Preview feature: how long, in seconds (60-172800), the carrier may keep attempting delivery before the message is marked expired. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.tags
array of object

Structured {name, value} labels for filtering and analytics. Tags become first-class query dimensions: filter the list endpoint by tag name, slice analytics by tag, and surface in webhook payloads. Maximum 20 tags per send. Use tags for low-cardinality dimensions (category, experiment_variant). For arbitrary structured context you do not need as a filter dimension, use metadata instead.

Show child parameters
messages.metadata
object

Arbitrary JSON object stored on the message, returned on API reads, and echoed in webhook payloads. Maximum 2 KB serialized. Use metadata for per-send context like internal IDs and foreign keys. For low-cardinality filterable labels, use tags instead.

messages.options
object

What Bird does to this message on its way out, such as smart_encoding. The message being relayed stays at the top level: its recipient, sender, content, and the delivery instructions the carrier acts on.

Show child parameters
messages.options.smart_encoding
boolean

Replace characters outside the GSM-7 alphabet with their closest GSM-7 equivalent before sending: typically curly quotes, dashes, ellipses, fullwidth forms, and non-breaking spaces.

One such character forces the whole body into UCS2, which more than halves the characters that fit in a segment, so replacing them often lowers the segment count and the cost.

Disabled by default, because it alters the body you composed. The replacement is all-or-nothing: a body that still holds a character outside the alphabet afterwards, such as an emoji or a non-Latin script, is sent exactly as you supplied it. Read the message back to see what was applied: text is the body as sent.

messages.options.track_clicks
boolean

Preview feature: link click tracking. Defaults to false. Currently unavailable; setting this to true returns 422 SMSUnsupportedFeature.

messages.options.max_price_per_segment
number

Preview feature: per-segment price ceiling. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.media_urls
array of string

Preview feature: multimedia (MMS) attachments. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.messaging_profile_id
string

Preview feature: sender selection from a messaging profile pool. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.scheduled_at
string

Preview feature: send-later scheduling. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.broadcast_id
string

Preview feature: broadcast correlation. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.campaign_id
string

Preview feature: campaign correlation for analytics. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.audience_id
string

Preview feature: audience-targeted sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.contact_id
string

Preview feature: contact-targeted sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.topic_id
string

Preview feature: topic-gated sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

messages.personalization
object

Preview feature: per-recipient substitution for batch sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.

Response Payload

data
array of object
required

One entry per message in the batch, in submission order.

Show child attributes
summary
object
required

Aggregate result for the batch.

Show child attributes

Continue with the documentation, guides and examples for this topic.