SMS
Send Bulk SMS

Send Bulk SMS

Send the same SMS message to up to 10,000 recipients in a single API call. Each recipient is queued independently — a failure for one number does not stop the others.


Endpoint

POST /v1/sms/send-bulk/

Authentication: Bearer API key


Request Body

{
  "recipients": ["254712345678", "254723456789", "254734567890"],
  "message":    "Hi! Your invoice is ready. Log in to view it.",
  "sender_id":  "TALKNTALK"
}
FieldTypeRequiredDescription
recipientsstring[]YesArray of phone numbers in international format. Duplicates are removed automatically. Max 10,000.
messagestringYesSMS body sent to every recipient
sender_idstringNoSender name. Defaults to your organisation's default sender ID.

Request

curl -X POST https://api.talkntalk.africa/v1/sms/send-bulk/ \
  -H "Authorization: Bearer tk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "recipients": ["254712345678", "254723456789"],
    "message":    "Your promo code is SAVE20. Valid today only!",
    "sender_id":  "TALKNTALK"
  }'

Response

201 Created — all messages queued successfully

207 Multi-Status — some messages failed (e.g. insufficient balance)

{
  "queued":    2,
  "failed":    1,
  "total":     3,
  "sender_id": "TALKNTALK",
  "results": [
    { "id": "018f3a2b-...", "mobile": "254712345678", "status": "queued" },
    { "id": "018f3a2c-...", "mobile": "254723456789", "status": "queued" }
  ],
  "errors": [
    { "mobile": "254734567890", "error": "insufficient_balance", "detail": "Insufficient balance. Required KES 1.00, available KES 0.00." }
  ]
}
FieldDescription
queuedNumber of messages successfully queued
failedNumber that could not be sent
totalTotal unique recipients after deduplication
resultsPer-number success list with message id and status
errorsPer-number failure list with error code and detail

Balance exhaustion behaviour

If the wallet runs out of funds mid-batch, processing stops immediately. All remaining recipients are marked skipped:

{ "mobile": "254799999999", "error": "skipped", "detail": "Processing stopped due to insufficient balance." }

Code Examples

Node.js

const res = await fetch('https://api.talkntalk.africa/v1/sms/send-bulk/', {
  method:  'POST',
  headers: {
    'Authorization': 'Bearer tk_live_xxxx',
    'Content-Type':  'application/json',
  },
  body: JSON.stringify({
    recipients: ['254712345678', '254723456789', '254734567890'],
    message:    'Your promo code is SAVE20!',
    sender_id:  'TALKNTALK',
  }),
});
const data = await res.json();
console.log(`Queued: ${data.queued}/${data.total}`);
data.errors.forEach(e => console.warn(`Failed: ${e.mobile}${e.detail}`));

Python

import requests
 
recipients = ['254712345678', '254723456789', '254734567890']
 
r = requests.post(
    'https://api.talkntalk.africa/v1/sms/send-bulk/',
    headers={'Authorization': 'Bearer tk_live_xxxx'},
    json={
        'recipients': recipients,
        'message':    'Your promo code is SAVE20!',
        'sender_id':  'TALKNTALK',
    },
)
data = r.json()
print(f"Queued: {data['queued']}/{data['total']}")
for err in data.get('errors', []):
    print(f"Failed {err['mobile']}: {err['detail']}")

PHP

$recipients = ['254712345678', '254723456789'];
 
$ch = curl_init('https://api.talkntalk.africa/v1/sms/send-bulk/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer tk_live_xxxx',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'recipients' => $recipients,
        'message'    => 'Your promo code is SAVE20!',
        'sender_id'  => 'TALKNTALK',
    ]),
]);
$data = json_decode(curl_exec($ch), true);
echo "Queued: {$data['queued']}/{$data['total']}\n";

Tracking your campaign

Use the campaign_id returned in the response to monitor delivery:

What you needEndpoint
Overall progress (sent, failed, delivery rate)Campaign Status
Per-recipient delivery statusCampaign Messages

Rate Limits

This endpoint is limited to 60 requests per minute per organisation. Each call can contain up to 10,000 recipients, so this is rarely a bottleneck. See Rate Limits for details.


Error Responses

StatusCodeDescription
400 Bad Requestrecipients is empty, exceeds 10,000, or message is missing
401 Unauthorizedauthentication_failedMissing or invalid API key
207 Multi-StatusPartial success — check errors array
429 Too Many RequestsRate limit exceeded — retry after the Retry-After header