Skip to main content
500CallsDocs
Browse the docs

Sending SMS

There are three ways to send. All three need the messages:send scope, take JSON and answer 202 Accepted.

EndpointUse it for
POST /v1/sms/messagesOne message to one recipient, such as a login code
POST /v1/sms/messages/bulkThe same text to many recipients
POST /v1/sms/messages/batchA different text for each recipient, one sender ID

Send one message

cURL
curl -X POST 'https://api.messaging.500calls.com/v1/sms/messages' \  -H "Authorization: Bearer $API_KEY_500CALLS" \  -H 'Content-Type: application/json' \  -H 'Idempotency-Key: order-78123-otp' \  -d '{  "to": "08031234567",  "from": "ACME",  "body": "Your ACME code is 482913. It expires in 5 minutes.",  "client_reference": "order-78123"}'
Python
import osimport requestsresponse = requests.post(    "https://api.messaging.500calls.com/v1/sms/messages",    headers={        "Authorization": f"Bearer {os.environ['API_KEY_500CALLS']}",        "Idempotency-Key": "order-78123-otp",    },    json={        "to": "08031234567",        "from": "ACME",        "body": "Your ACME code is 482913. It expires in 5 minutes.",        "client_reference": "order-78123",    },    timeout=30,)print(response.status_code, response.json())
Node.js
const response = await fetch("https://api.messaging.500calls.com/v1/sms/messages", {  method: "POST",  headers: {    Authorization: `Bearer ${process.env.API_KEY_500CALLS}`,    "Content-Type": "application/json",    "Idempotency-Key": "order-78123-otp",  },  body: JSON.stringify({    "to": "08031234567",    "from": "ACME",    "body": "Your ACME code is 482913. It expires in 5 minutes.",    "client_reference": "order-78123"  }),});console.log(response.status, await response.json());
PHP
<?php$curl = curl_init('https://api.messaging.500calls.com/v1/sms/messages');curl_setopt_array($curl, [    CURLOPT_CUSTOMREQUEST => 'POST',    CURLOPT_HTTPHEADER => [        'Authorization: Bearer ' . getenv('API_KEY_500CALLS'),        'Content-Type: application/json',        'Idempotency-Key: order-78123-otp',    ],    CURLOPT_POSTFIELDS => json_encode([        'to' => '08031234567',        'from' => 'ACME',        'body' => 'Your ACME code is 482913. It expires in 5 minutes.',        'client_reference' => 'order-78123',    ]),    CURLOPT_RETURNTRANSFER => true,]);$body = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);echo $status, PHP_EOL, $body, PHP_EOL;

to takes any common format, such as 08031234567, 2348031234567 or +234 803 123 4567, and comes back in E.164 format, +2348031234567. from is your sender ID, and it must be one you can send from now (see sender IDs). body is sent exactly as you give it. Add client_reference, up to 64 printable ASCII characters, to tie the message to your own records; you can list messages by it.

Send to many recipients

POST /v1/sms/messages/bulk takes a to array, up to 1,000 recipients by default. Each recipient is checked on its own:

  • A recipient that passes becomes a queued message.
  • A recipient that fails becomes a rejected message with its own ID and an error, such as invalid_recipient, and is never charged.
  • When the same number appears twice, the first one wins and the later ones are rejected as duplicate_recipient.

The response is a send result: totals, the amount held, and one entry per recipient in the order you sent them.

cURL
curl -X POST 'https://api.messaging.500calls.com/v1/sms/messages/bulk' \  -H "Authorization: Bearer $API_KEY_500CALLS" \  -H 'Content-Type: application/json' \  -H 'Idempotency-Key: order-78123-otp' \  -d '{  "to": [    "08031234567",    "+2348059876543",    "0803 123 4567",    "12345",    "+447700900123"  ],  "from": "ACMESTORES",  "body": "Hi! Our Lekki store opens at 9am tomorrow."}'
Python
import osimport requestsresponse = requests.post(    "https://api.messaging.500calls.com/v1/sms/messages/bulk",    headers={        "Authorization": f"Bearer {os.environ['API_KEY_500CALLS']}",        "Idempotency-Key": "order-78123-otp",    },    json={        "to": [            "08031234567",            "+2348059876543",            "0803 123 4567",            "12345",            "+447700900123",        ],        "from": "ACMESTORES",        "body": "Hi! Our Lekki store opens at 9am tomorrow.",    },    timeout=30,)print(response.status_code, response.json())
Node.js
const response = await fetch("https://api.messaging.500calls.com/v1/sms/messages/bulk", {  method: "POST",  headers: {    Authorization: `Bearer ${process.env.API_KEY_500CALLS}`,    "Content-Type": "application/json",    "Idempotency-Key": "order-78123-otp",  },  body: JSON.stringify({    "to": [      "08031234567",      "+2348059876543",      "0803 123 4567",      "12345",      "+447700900123"    ],    "from": "ACMESTORES",    "body": "Hi! Our Lekki store opens at 9am tomorrow."  }),});console.log(response.status, await response.json());
PHP
<?php$curl = curl_init('https://api.messaging.500calls.com/v1/sms/messages/bulk');curl_setopt_array($curl, [    CURLOPT_CUSTOMREQUEST => 'POST',    CURLOPT_HTTPHEADER => [        'Authorization: Bearer ' . getenv('API_KEY_500CALLS'),        'Content-Type: application/json',        'Idempotency-Key: order-78123-otp',    ],    CURLOPT_POSTFIELDS => json_encode([        'to' => [            '08031234567',            '+2348059876543',            '0803 123 4567',            '12345',            '+447700900123',        ],        'from' => 'ACMESTORES',        'body' => 'Hi! Our Lekki store opens at 9am tomorrow.',    ]),    CURLOPT_RETURNTRANSFER => true,]);$body = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);echo $status, PHP_EOL, $body, PHP_EOL;

Send a personalised batch

POST /v1/sms/messages/batch takes a messages array, each item with its own to, body and optional client_reference, and one from for all of them. Fill in names and other details yourself: the API sends each body exactly as given and does not fill in placeholders such as {{name}}. An item whose body is longer than 10 segments is rejected on its own, and the rest go ahead.

cURL
curl -X POST 'https://api.messaging.500calls.com/v1/sms/messages/batch' \  -H "Authorization: Bearer $API_KEY_500CALLS" \  -H 'Content-Type: application/json' \  -H 'Idempotency-Key: order-78123-otp' \  -d '{  "from": "ACME",  "messages": [    {      "to": "08031234567",      "body": "Hello Ada, your balance is ₦12,500.00.",      "client_reference": "acct-1001"    },    {      "to": "08129876543",      "body": "Hello Chinedu, your balance is ₦3,200.00.",      "client_reference": "acct-1002"    }  ]}'
Python
import osimport requestsresponse = requests.post(    "https://api.messaging.500calls.com/v1/sms/messages/batch",    headers={        "Authorization": f"Bearer {os.environ['API_KEY_500CALLS']}",        "Idempotency-Key": "order-78123-otp",    },    json={        "from": "ACME",        "messages": [            {                "to": "08031234567",                "body": "Hello Ada, your balance is ₦12,500.00.",                "client_reference": "acct-1001",            },            {                "to": "08129876543",                "body": "Hello Chinedu, your balance is ₦3,200.00.",                "client_reference": "acct-1002",            },        ],    },    timeout=30,)print(response.status_code, response.json())
Node.js
const response = await fetch("https://api.messaging.500calls.com/v1/sms/messages/batch", {  method: "POST",  headers: {    Authorization: `Bearer ${process.env.API_KEY_500CALLS}`,    "Content-Type": "application/json",    "Idempotency-Key": "order-78123-otp",  },  body: JSON.stringify({    "from": "ACME",    "messages": [      {        "to": "08031234567",        "body": "Hello Ada, your balance is ₦12,500.00.",        "client_reference": "acct-1001"      },      {        "to": "08129876543",        "body": "Hello Chinedu, your balance is ₦3,200.00.",        "client_reference": "acct-1002"      }    ]  }),});console.log(response.status, await response.json());
PHP
<?php$curl = curl_init('https://api.messaging.500calls.com/v1/sms/messages/batch');curl_setopt_array($curl, [    CURLOPT_CUSTOMREQUEST => 'POST',    CURLOPT_HTTPHEADER => [        'Authorization: Bearer ' . getenv('API_KEY_500CALLS'),        'Content-Type: application/json',        'Idempotency-Key: order-78123-otp',    ],    CURLOPT_POSTFIELDS => json_encode([        'from' => 'ACME',        'messages' => [            [                'to' => '08031234567',                'body' => 'Hello Ada, your balance is ₦12,500.00.',                'client_reference' => 'acct-1001',            ],            [                'to' => '08129876543',                'body' => 'Hello Chinedu, your balance is ₦3,200.00.',                'client_reference' => 'acct-1002',            ],        ],    ]),    CURLOPT_RETURNTRANSFER => true,]);$body = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);echo $status, PHP_EOL, $body, PHP_EOL;

When nothing can be sent

The whole request fails, and nothing is saved or charged, when:

  • a field every recipient shares is wrong, such as an empty body or a from you cannot use: 422;
  • no recipient passes its checks: 422 validation_error, with one entry in errors per recipient, such as to[3] or messages[3].to;
  • your available balance does not cover every recipient that passed: 402 insufficient_balance. A send is all or nothing.

Encoding and segments

A message is sent in segments, and you pay per segment. A body that uses only the GSM 7-bit alphabet fits 160 characters in one segment and 153 in each segment after that. A body with any other character, such as the naira sign ₦ or an emoji, is sent as UCS-2: 70 characters in one segment and 67 in each after that. A body can be at most 10 segments long. Each message reports its encoding and segments.

What a message costs

When a message is accepted, we hold the most it can cost from your balance: its segments times the highest price among the routes that can carry it. That is its held_amount. When it is sent, you are charged the price of the route that carried it, its amount, which is never more than the hold, and the rest returns to your available balance within about a minute. A message that is rejected, fails or is cancelled is not charged. A message that is sent but not delivered keeps its charge.

Schedule a send

Add send_at, an RFC 3339 time between 1 minute and 90 days from now, to send later. The message stays scheduled until then, with its funds held from the moment it is accepted, and you can cancel it until it is sent.