# Bulk SMS API reference

Reference for the BulkSMSAPI REST API: base URL, authentication, endpoints for messages, batches, estimates, balance and prices, webhooks, errors and limits.

BulkSMSAPI is a bulk SMS API for developers: send one text or a batch of up to 10,000 numbers per request to 227 countries over REST, follow every message with signed webhooks, and pay per message from a prepaid balance. This reference covers every endpoint, field, event and error the API has. Every page is also available as Markdown: add `.md` to its address.

## Base URL and format

All requests go to `https://bulksmsapi.cc/v1` over HTTPS. Bodies are JSON (`Content-Type: application/json`, UTF-8), and every answer is JSON, including errors. Times are ISO 8601 in UTC. Money is a string in US dollars with five decimals (`"0.00507"`), so prices below a cent are exact.

## Endpoints

| Method and path | What it does | Key |
| --- | --- | --- |
| POST /v1/messages | Send one text to one number | Yes |
| POST /v1/batches | Send to up to 10,000 numbers, one text or one per number | Yes |
| GET /v1/messages/{id} | One message with its status and price | Yes |
| GET /v1/messages | Messages, newest first, filter by batch or status | Yes |
| GET /v1/batches/{id} | One batch with counts per status | Yes |
| GET /v1/batches | Batches, newest first | Yes |
| POST /v1/batches/{id}/cancel | Cancel a scheduled batch, full refund | Yes |
| POST /v1/estimates | Price a request without sending it | Yes |
| GET /v1/balance | Balance in USD | Yes |
| GET /v1/pricing | Price per segment for every country | No |
| GET /v1/pricing/{country} | Price for one country (ISO code) | No |

## A first request

Create an API key in the console, export it, and send a message. The answer is 202 with the message object: the cost is taken from the balance and the message is queued.

```bash
curl https://bulksmsapi.cc/v1/messages \
  -H "Authorization: Bearer $BULKSMSAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155550123",
    "text": "Your Northwind code is 482913. It expires in 10 minutes.",
    "reference": "login-7781"
  }'
```

```javascript
// Node.js 18 or newer (built-in fetch)
const res = await fetch("https://bulksmsapi.cc/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BULKSMSAPI_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+14155550123",
    text: "Your Northwind code is 482913. It expires in 10 minutes.",
    reference: "login-7781",
  }),
});
const message = await res.json();
console.log(res.status, message.id, message.status, message.price);
```

```python
# Python 3, standard library only
import json, os, urllib.request

req = urllib.request.Request(
    "https://bulksmsapi.cc/v1/messages",
    data=json.dumps({
        "to": "+14155550123",
        "text": "Your Northwind code is 482913. It expires in 10 minutes.",
        "reference": "login-7781",
    }).encode(),
    headers={
        "Authorization": "Bearer " + os.environ["BULKSMSAPI_KEY"],
        "Content-Type": "application/json",
        "User-Agent": "northwind-app/1.0",
    },
    method="POST",
)
with urllib.request.urlopen(req) as res:
    message = json.load(res)
print(res.status, message["id"], message["status"], message["price"])
```

```php
<?php
// PHP 7.4 or newer with the curl extension
$ch = curl_init("https://bulksmsapi.cc/v1/messages");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("BULKSMSAPI_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "to" => "+14155550123",
        "text" => "Your Northwind code is 482913. It expires in 10 minutes.",
        "reference" => "login-7781",
    ]),
]);
$message = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $message["id"], " ", $message["status"], "\n";
```

```go
// Go 1.20 or newer, standard library only
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"to":        "+14155550123",
		"text":      "Your Northwind code is 482913. It expires in 10 minutes.",
		"reference": "login-7781",
	})
	req, _ := http.NewRequest("POST", "https://bulksmsapi.cc/v1/messages", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("BULKSMSAPI_KEY"))
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var message map[string]any
	json.NewDecoder(res.Body).Decode(&message)
	fmt.Println(res.StatusCode, message["id"], message["status"], message["price"])
}
```

202 Accepted:
```json
{
  "id": "msg_8F3K2Q9TZ1MX",
  "object": "message",
  "batch_id": "bat_4M7Q1X0PK2RS",
  "to": "+14155550123",
  "country": "US",
  "text": "Your Northwind code is 482913. It expires in 10 minutes.",
  "segments": 1,
  "price": "0.00507",
  "currency": "USD",
  "status": "queued",
  "error": null,
  "reference": "login-7781",
  "created_at": "2026-11-07T09:30:12.418Z",
  "sent_at": null,
  "done_at": null
}
```

## How a message moves

Accepted messages are `queued` (or `scheduled` when the request has send_at). The queue hands them to the network every two seconds, up to 1,200 per minute per account, and they become `sent`. The final status is `delivered` or `failed`; a failed message is refunded to the balance at once. Each final status calls your webhook with a signed event.

## Limits at a glance

| Limit | Value |
| --- | --- |
| Recipients per API request | 10,000 |
| Recipients per console campaign | 50,000 |
| Requests per minute per key | 600 |
| Messages handed to the network per minute per account | 1,200 |
| Longest text | 10 segments (1,530 GSM-7 or 670 UCS-2 characters) |
| Schedule window | 2 minutes to 45 days ahead |
| Webhook attempts per event | 10 |
| Idempotency-Key memory | 24 hours |
| API keys per account | 10 |

## Machine-readable files

- [OpenAPI 3.1 spec](https://bulksmsapi.cc/openapi.json) for code generators and API clients.
- [llms-full.txt](https://bulksmsapi.cc/llms-full.txt): this whole reference as one plain text file.
- Every docs page as Markdown, for example [/en/docs/webhooks.md](https://bulksmsapi.cc/en/docs/webhooks.md).
