Create a batch of SMS messages
/v1/sms/batchesconst result = await bird.sms.sendBatch({
messages: [
{
from: "+15557654321",
to: "+15551111111",
text: "Hi Alice!",
category: "marketing",
},
{
from: "+15557654321",
to: "+15552222222",
text: "Hi Bob!",
category: "marketing",
},
],
});batch = client.sms.send_batch(
messages=[
{
"from_": "+15557654321",
"to": "+15551111111",
"text": "Hi Alice!",
"category": "marketing",
},
{
"from_": "+15557654321",
"to": "+15552222222",
"text": "Hi Bob!",
"category": "marketing",
},
]
)
for msg in batch.data:
print(msg.id, msg.status)batch, err := client.Sms.SendBatch(context.Background(), bird.SmsSendBatchParams{
Messages: []bird.SmsSendParams{
{
From: "+15557654321", To: "+15551111111",
Text: "Hi Alice!", Category: bird.SMSCategoryMarketing,
},
{
From: "+15557654321", To: "+15552222222",
Text: "Hi Bob!", Category: bird.SMSCategoryMarketing,
},
},
})
if err != nil {
log.Fatal(err)
}
for _, msg := range batch.Data {
fmt.Println(msg.Id, *msg.Status)
}$batch = $bird->sms->sendBatch(messages: [
(new SMSMessageSendRequest())
->setFrom('+15557654321')
->setTo('+15551111111')
->setText('Hi Alice!')
->setCategory('marketing'),
(new SMSMessageSendRequest())
->setFrom('+15557654321')
->setTo('+15552222222')
->setText('Hi Bob!')
->setCategory('marketing'),
]);
foreach ($batch->getData() ?? [] as $item) {
echo $item->getId(), ' ', $item->getStatus(), "\n";
}curl -X POST "https://us1.platform.bird.com/v1/sms/batches" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+15551111111",
"from": "+15557654321",
"text": "Hi Alice!",
"category": "marketing"
},
{
"to": "+15552222222",
"from": "+15557654321",
"text": "Hi Bob!",
"category": "marketing"
}
]
}'{
"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
messagesSMS message send requests, up to 100. Each is an independent send; all are validated before any is queued.
Show child parameters
messages.toRecipient 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.fromSender 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.textFree-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.categoryContent 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_periodPreview 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.tagsStructured {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.
messages.metadataArbitrary 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.optionsWhat 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.
messages.media_urlsPreview feature: multimedia (MMS) attachments. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.messaging_profile_idPreview feature: sender selection from a messaging profile pool. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.scheduled_atPreview feature: send-later scheduling. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.broadcast_idPreview feature: broadcast correlation. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.campaign_idPreview feature: campaign correlation for analytics. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.audience_idPreview feature: audience-targeted sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.contact_idPreview feature: contact-targeted sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.topic_idPreview feature: topic-gated sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.personalizationPreview feature: per-recipient substitution for batch sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.toRecipient 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.fromSender 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.categoryContent 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_periodPreview 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.tagsStructured {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.
messages.metadataArbitrary 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.optionsWhat 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.
messages.media_urlsPreview feature: multimedia (MMS) attachments. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.messaging_profile_idPreview feature: sender selection from a messaging profile pool. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.scheduled_atPreview feature: send-later scheduling. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.templateSend using a stored template instead of free text. The category is derived from the template, so category and media_urls are rejected. A workspace template requires from; a built-in template selects its sender and rejects from.
messages.broadcast_idPreview feature: broadcast correlation. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.campaign_idPreview feature: campaign correlation for analytics. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.audience_idPreview feature: audience-targeted sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.contact_idPreview feature: contact-targeted sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.topic_idPreview feature: topic-gated sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
messages.personalizationPreview feature: per-recipient substitution for batch sends. Currently unavailable; supplying this field returns 422 SMSUnsupportedFeature.
Response Payload
dataOne entry per message in the batch, in submission order.
Related resources
Continue with the documentation, guides and examples for this topic.