Bulk SMS
Send the same message to multiple recipients, or unique messages to different recipients in a single API call.
Static Bulk SMS (Same Message)
Send an identical message to multiple phone numbers in one request. Uses the same endpoint as Send SMS — pass To as a repeated array parameter.
Parameters
All Send SMS parameters apply. The key difference is To, which must be passed as an array:
| Parameter | Required | Description |
|---|---|---|
From | Mandatory | ExoPhone or approved Sender ID |
To[] | Mandatory | Repeat this parameter for each recipient number |
Body | Mandatory | Message content (max 2000 characters) |
Examples
- cURL
- Python
- Node.js
- PHP
curl -X POST https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/send \
-d "From=0XXXXXX4890" \
-d "To[]=+919876543210" \
-d "To[]=+919876543211" \
-d "Body=Flash sale! 50% off all items today." \
-d "DltEntityId=1234567890"
import requests
data = [
('From', '0XXXXXX4890'),
('To[]', '+919876543210'),
('To[]', '+919876543211'),
('Body', 'Flash sale! 50% off all items today.'),
('DltEntityId', '1234567890'),
]
response = requests.post(
'https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/send',
data=data
)
print(response.json())
const url = `https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/send`;
const body = new URLSearchParams();
body.append('From', '0XXXXXX4890');
body.append('To[]', '+919876543210');
body.append('To[]', '+919876543211');
body.append('Body', 'Flash sale! 50% off all items today.');
body.append('DltEntityId', '1234567890');
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
});
const data = await response.json();
console.log(data);
<?php
$data = http_build_query([
'From' => '0XXXXXX4890',
'Body' => 'Flash sale! 50% off all items today.',
'DltEntityId' => '1234567890',
]) . '&To%5B%5D=+919876543210&To%5B%5D=+919876543211';
$ch = curl_init('https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/send');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
URL Shortening Example
curl -X POST https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/send \
-d "From=0XXXXXX4890" \
-d "To[]=+919876543210" \
-d "To[]=+919876543211" \
-d "Body=Sale at https://example.com/sale" \
-d "ShortenUrl=true" \
-d "ShortenUrlParams[Header]=EXOTEL" \
-d "ShortenUrlParams[Tracking]=true" \
-d "ShortenUrlParams[ClickTrackingCallbackUrl]=https://your-server.com/clicks"
Response
Success (HTTP 200): Returns an array of SMSMessage objects, one per recipient.
[
{
"SMSMessage": {
"Sid": "0f477d60517e6e6a0f6d9a7e9af8630e",
"AccountSid": "Exotel",
"From": "0XXXXXX4890/WEBDEV",
"To": "0XXXXX30240",
"DateCreated": "2017-03-03 14:14:20",
"DateUpdated": "2017-03-03 14:14:20",
"DateSent": null,
"Body": "Flash sale! 50% off all items today.",
"Direction": "outbound-api",
"Uri": "/v1/Accounts/Exotel/SMS/Messages/0f477d60517e6e6a0f6d9a7e9af8630e.json",
"ApiVersion": null,
"Price": null,
"Status": "queued",
"DetailedStatusCode": 21010,
"DetailedStatus": "PENDING_TO_OPERATOR",
"SmsUnits": null
}
}
]
Partial success (HTTP 207): Returned when some recipients succeeded and others failed. Failed entries have "Status": "failed" and null Sid.
[
{
"SMSMessage": {
"Sid": null,
"AccountSid": "Exotel",
"From": null,
"To": null,
"DateCreated": null,
"DateUpdated": null,
"DateSent": null,
"Body": "Flash sale! 50% off all items today.",
"Direction": null,
"Uri": null,
"ApiVersion": null,
"Price": null,
"Status": "failed",
"SmsUnits": null,
"DetailedStatusCode": 23010,
"DetailedStatus": "FAILED_INVALID_DESTINATION_NUMBER"
}
},
{
"SMSMessage": {
"Sid": "1393e66deb633e7297bcfa9a9da0bb37",
"AccountSid": "Exotel",
"From": "0XXXXXX4890/WEBDEV",
"To": "+919876543211",
"DateCreated": "2017-03-03 14:14:20",
"DateUpdated": "2017-03-03 14:14:20",
"DateSent": null,
"Body": "Flash sale! 50% off all items today.",
"Direction": "outbound-api",
"Uri": "/v1/Accounts/Exotel/SMS/Messages/1393e66deb633e7297bcfa9a9da0bb37.json",
"ApiVersion": null,
"Price": null,
"Status": "queued",
"DetailedStatusCode": 21010,
"DetailedStatus": "PENDING_TO_OPERATOR",
"SmsUnits": null
}
}
]
Dynamic Bulk SMS (Unique Messages)
Send personalized messages to different recipients in a single request. Uses the /bulksend endpoint with form-encoded Messages[] array. Maximum 100 messages per request.
Parameters
| Parameter | Required | Description |
|---|---|---|
From | Optional | Default ExoPhone or Sender ID. Used when From is not set in a Messages entry. |
Messages | Mandatory | Array of message objects (max 100). Each object must include To and Body. |
Messages[N][To] | Mandatory | Recipient number for message N |
Messages[N][Body] | Mandatory | Message content for message N (max 2000 chars) |
Messages[N][From] | Optional | Per-message Sender ID; falls back to outer From |
Messages[N][SmsType] | Optional | Per-message SMS type: transactional, transactional_opt_in, promotional |
Messages[N][DltTemplateId] | Optional | Per-message DLT Template ID (India) |
EncodingType | Optional | plain or unicode |
ShortenUrl | Optional | true to shorten URLs |
ShortenUrlParams[Header] | Optional | DLT-registered header for the short URL domain |
ShortenUrlParams[Tracking] | Optional | true to enable click tracking |
ShortenUrlParams[ClickTrackingCallbackUrl] | Optional | Webhook URL for click events |
StatusCallback | Optional | URL to receive delivery status per message |
DltEntityId | Optional (Mandatory for India) | DLT Entity ID applied to all messages |
CustomField | Optional | Custom reference field |
Priority | Optional | normal or high |
Examples
- cURL
- Python
- Node.js
- PHP
curl -X POST https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/bulksend \
-d "From=0XXXXXX4890" \
-d "Messages[0][Body]=Hi Rahul, your order #1234 has shipped." \
-d "Messages[0][To]=+919876543210" \
-d "Messages[1][Body]=Hi Priya, your order #5678 has shipped." \
-d "Messages[1][To]=+919876543211"
import requests
data = [
('From', '0XXXXXX4890'),
('Messages[0][Body]', 'Hi Rahul, your order #1234 has shipped.'),
('Messages[0][To]', '+919876543210'),
('Messages[1][Body]', 'Hi Priya, your order #5678 has shipped.'),
('Messages[1][To]', '+919876543211'),
]
response = requests.post(
'https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/bulksend',
data=data
)
print(response.json())
const url = `https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/bulksend`;
const body = new URLSearchParams();
body.append('From', '0XXXXXX4890');
body.append('Messages[0][Body]', 'Hi Rahul, your order #1234 has shipped.');
body.append('Messages[0][To]', '+919876543210');
body.append('Messages[1][Body]', 'Hi Priya, your order #5678 has shipped.');
body.append('Messages[1][To]', '+919876543211');
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
});
const data = await response.json();
console.log(data);
<?php
$data = http_build_query([
'From' => '0XXXXXX4890',
'Messages' => [
['Body' => 'Hi Rahul, your order #1234 has shipped.', 'To' => '+919876543210'],
['Body' => 'Hi Priya, your order #5678 has shipped.', 'To' => '+919876543211'],
],
]);
$ch = curl_init('https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/bulksend');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
URL Shortening Example
curl -X POST https://<api_key>:<api_token><subdomain>/v1/Accounts/<your_sid>/Sms/bulksend \
-d "From=0XXXXXX4890" \
-d "Messages[0][Body]=Hi Rahul, your deal: https://example.com/deal/1234" \
-d "Messages[0][To]=+919876543210" \
-d "Messages[1][Body]=Hi Priya, your deal: https://example.com/deal/5678" \
-d "Messages[1][To]=+919876543211" \
-d "ShortenUrl=true" \
-d "ShortenUrlParams[Header]=EXOTEL" \
-d "ShortenUrlParams[Tracking]=true" \
-d "ShortenUrlParams[ClickTrackingCallbackUrl]=https://your-server.com/clicks"
Response
Success (HTTP 200):
[
{
"SMSMessage": {
"Sid": "0f477d60517e6e6a0f6d9a7e9af8630e",
"AccountSid": "Exotel",
"From": "0XXXXXX4890/WEBDEV",
"To": "+919876543210",
"DateCreated": "2017-03-03 14:14:20",
"DateUpdated": "2017-03-03 14:14:20",
"DateSent": null,
"Body": "Hi Rahul, your order #1234 has shipped.",
"Direction": "outbound-api",
"Uri": "/v1/Accounts/Exotel/SMS/Messages/0f477d60517e6e6a0f6d9a7e9af8630e.json",
"ApiVersion": null,
"Price": null,
"Status": "queued",
"DetailedStatusCode": 21010,
"DetailedStatus": "PENDING_TO_OPERATOR",
"SmsUnits": null
}
},
{
"SMSMessage": {
"Sid": "1393e66deb633e7297bcfa9a9da0bb37",
"AccountSid": "Exotel",
"From": "0XXXXXX4890/WEBDEV",
"To": "+919876543211",
"DateCreated": "2017-03-03 14:14:20",
"DateUpdated": "2017-03-03 14:14:20",
"DateSent": null,
"Body": "Hi Priya, your order #5678 has shipped.",
"Direction": "outbound-api",
"Uri": "/v1/Accounts/Exotel/SMS/Messages/1393e66deb633e7297bcfa9a9da0bb37.json",
"ApiVersion": null,
"Price": null,
"Status": "queued",
"DetailedStatusCode": 21010,
"DetailedStatus": "PENDING_TO_OPERATOR",
"SmsUnits": null
}
}
]
Partial success (HTTP 207): Some messages failed. Failed entries have "Status": "failed" with a DetailedStatusCode and null Sid.
[
{
"SMSMessage": {
"Sid": null,
"AccountSid": "Exotel",
"From": null,
"To": "+919876543210",
"DateCreated": null,
"DateUpdated": null,
"DateSent": null,
"Body": "",
"Direction": null,
"Uri": null,
"ApiVersion": null,
"Price": null,
"Status": "failed",
"DetailedStatusCode": 23000,
"DetailedStatus": "FAILED_MISSING_BODY",
"SmsUnits": null
}
},
{
"SMSMessage": {
"Sid": "1393e66deb633e7297bcfa9a9da0bb37",
"AccountSid": "Exotel",
"From": "0XXXXXX4890/WEBDEV",
"To": "+919876543211",
"DateCreated": "2017-03-03 14:14:20",
"DateUpdated": "2017-03-03 14:14:20",
"DateSent": null,
"Body": "Hi Priya, your order #5678 has shipped.",
"Direction": "outbound-api",
"Uri": "/v1/Accounts/Exotel/SMS/Messages/1393e66deb633e7297bcfa9a9da0bb37.json",
"ApiVersion": null,
"Price": null,
"Status": "queued",
"DetailedStatusCode": 21010,
"DetailedStatus": "PENDING_TO_OPERATOR",
"SmsUnits": null
}
}
]
Limits
| Limit | Value |
|---|---|
| Max recipients — static bulk | 100 numbers |
| Max messages — dynamic bulk | 100 messages |
| API error code | 40406 (static) / 40400 (dynamic) if exceeded |
Each message in a bulk request is processed independently. Individual messages may succeed or fail without affecting others.