Skip to main content

Bulk SMS

Send messages to many recipients in a single API call. Bulk send is optimised for high-throughput campaigns, multi-recipient alerts, and personalised batches, and it returns a per-recipient breakdown so you can track each message independently.

๐Ÿ“ก Endpointโ€‹

POST /v1/sms/bulk

Base URL: https://api.afriroute.ai

๐Ÿ”‘ Authenticationโ€‹

Authorization: Bearer $AFRIROUTE_API_KEY
Content-Type: application/json

๐Ÿ“ฅ Request Parametersโ€‹

FieldTypeRequiredDescription
fromstringYesDefault sender ID for all messages in the batch
messagesarrayYesList of message objects (max 1,000 per request)
messages[].tostringYesRecipient in E.164 format
messages[].textstringYesMessage content for this recipient
messages[].fromstringNoPer-message sender ID override
callback_urlstringNoWebhook URL for delivery reports on all messages
scheduled_atstringNoISO 8601 timestamp to schedule the whole batch

๐Ÿ“ค Example Requestโ€‹

curl -X POST https://api.afriroute.ai/api/v1/sms/bulk \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "AFRIROUTE",
"callback_url": "https://yourapp.com/webhooks/sms",
"messages": [
{ "to": "+251911111111", "text": "Hi Alice, order #123 shipped!" },
{ "to": "+251922222222", "text": "Hi Bob, your payment was received!" }
]
}'
const response = await fetch('https://api.afriroute.ai/api/v1/sms/bulk', {
method: 'POST',
headers: {
'Authorization': 'Bearer $AFRIROUTE_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
from: 'AFRIROUTE',
callback_url: 'https://yourapp.com/webhooks/sms',
messages: [
{ to: '+251911111111', text: 'Hi Alice, order #123 shipped!' },
{ to: '+251922222222', text: 'Hi Bob, your payment was received!' }
]
})
});

const data = await response.json();
console.log(data.batch_id, data.accepted, data.rejected);
import requests

response = requests.post(
'https://api.afriroute.ai/api/v1/sms/bulk',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
json={
'from': 'AFRIROUTE',
'callback_url': 'https://yourapp.com/webhooks/sms',
'messages': [
{'to': '+251911111111', 'text': 'Hi Alice, order #123 shipped!'},
{'to': '+251922222222', 'text': 'Hi Bob, your payment was received!'},
],
},
)
print(response.json()['batch_id'])

โœ… Responseโ€‹

200 OK

{
"batch_id": "batch_7K8L9M0N",
"status": "queued",
"accepted": 2,
"rejected": 0,
"total_cost": 0.10,
"currency": "ETB",
"created_at": "2026-05-28T10:30:00Z",
"messages": [
{ "message_id": "msg_aaa111", "to": "+251911111111", "status": "queued", "parts": 1 },
{ "message_id": "msg_bbb222", "to": "+251922222222", "status": "queued", "parts": 1 }
]
}
FieldDescription
batch_idIdentifier for the whole batch
accepted / rejectedCount of valid vs invalid messages
messages[]Per-recipient result, each with its own message_id

๐Ÿ’ก Best Practicesโ€‹

  • Batch up to 1,000 messages per request; split larger campaigns into multiple calls.
  • Personalise the text per recipient where possible for higher engagement.
  • Set a single callback_url for the batch to track every message's delivery.
  • Use scheduled_at to send during local business hours.
  • Inspect rejected in the response โ€” rejected messages are not billed or sent.
  • Include opt-out instructions in marketing messages where legally required.

โš ๏ธ Error Handlingโ€‹

Individual invalid recipients are reported in the messages[] array with a failed status and an error code, while the rest of the batch still sends. A top-level 4xx is returned only when the whole request is malformed.

HTTPCodeResolution
400BATCH_TOO_LARGEReduce to 1,000 messages or fewer
400INVALID_PHONECorrect the offending recipient(s)
402INSUFFICIENT_BALANCETop up before sending large batches
429RATE_LIMITEDRetry with exponential backoff

See Error Codes for the complete list.


Last Updated: May 2026 ยท Need help? Contact Support โ†’