API reference
Send SMS with a single HTTP request. Messages are accepted immediately, queued, and delivered as soon as capacity allows โ your request never blocks waiting for a carrier.
Base URL
https://smsgateway.swiftcoder.in/api/v1
All paths below are relative to this. Requests and responses are JSON;
send Content-Type: application/json on anything with a body.
Authentication
Every request carries an API key. Pass it as a bearer token:
Authorization: Bearer sk_a1b2c3d4_your_key_here
Or, if a bearer header is inconvenient, as a dedicated header:
X-API-Key: sk_a1b2c3d4_your_key_here
messages:send to send, messages:read to read
back. A key without the right scope gets 403.
A key is a bearer credential: anyone holding it can send messages at your expense. Keep it server-side โ never in a browser, mobile binary, or public repository.
Send a message
POST /messages
Parameters
| Field | Type | Description |
|---|---|---|
| to | string, required | Recipient number. Use E.164 (+919140327455) โ spaces,
dashes and brackets are accepted and stripped. |
| body | string, required | Message text. Longer messages are split and billed by the carrier as multiple parts. |
| clientRef | string, optional | Your own reference for this message. Makes the request safe to retry โ see Idempotency. |
Request
curl -X POST https://smsgateway.swiftcoder.in/api/v1/messages \
-H "Authorization: Bearer $SMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919140327455",
"body": "Your verification code is 123456",
"clientRef": "signup-8842"
}'
const res = await fetch("https://smsgateway.swiftcoder.in/api/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "+919140327455",
body: "Your verification code is 123456",
clientRef: "signup-8842",
}),
});
if (!res.ok) throw new Error(`send failed: ${res.status}`);
const message = await res.json();
console.log(message.id, message.status);
import os, requests
res = requests.post(
"https://smsgateway.swiftcoder.in/api/v1/messages",
headers={"Authorization": f"Bearer {os.environ['SMS_API_KEY']}"},
json={
"to": "+919140327455",
"body": "Your verification code is 123456",
"clientRef": "signup-8842",
},
timeout=10,
)
res.raise_for_status()
message = res.json()
print(message["id"], message["status"])
$ch = curl_init("https://smsgateway.swiftcoder.in/api/v1/messages");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("SMS_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"to" => "+919140327455",
"body" => "Your verification code is 123456",
"clientRef" => "signup-8842",
]),
]);
$message = json_decode(curl_exec($ch), true);
echo $message["id"], " ", $message["status"];
Response 202 Accepted
{
"id": "6a9720e14e2f3b1b37d311d6",
"to": "+919140327455",
"body": "Your verification code is 123456",
"channel": "sms",
"status": "queued",
"clientRef": "signup-8842",
"attempts": 0,
"lastError": null,
"createdAt": "2026-09-01T19:01:02.627Z",
"sentAt": null,
"deliveredAt": null,
"failedAt": null
}
202 means accepted for delivery, not delivered. Keep the
id and poll it if you need the outcome.
Send in bulk
POST /messages/bulk
Up to 500 messages per request. Each entry takes the same fields as a single send and is validated on its own, so one bad number does not reject the batch.
curl -X POST https://smsgateway.swiftcoder.in/api/v1/messages/bulk \
-H "Authorization: Bearer $SMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "to": "+919140327455", "body": "Your order shipped" },
{ "to": "not-a-number", "body": "This one is rejected" }
]
}'
Response 207 Multi-Status
{
"results": [
{ "index": 0, "status": "accepted", "message": { "id": "6a97โฆ", "status": "queued" } },
{ "index": 1, "status": "rejected", "error": "invalid_recipient",
"message": "`to` must be a phone number, ideally in E.164 form (+15551234567)." }
]
}
Always 207, even if every entry succeeded. Read each result
by its index, which matches the order you sent.
Check a message
GET /messages/:id
Returns the message with its current state and timestamps.
curl https://smsgateway.swiftcoder.in/api/v1/messages/6a9720e14e2f3b1b37d311d6 \ -H "Authorization: Bearer $SMS_API_KEY"
{
"id": "6a9720e14e2f3b1b37d311d6",
"to": "+919140327455",
"status": "delivered",
"attempts": 1,
"lastError": null,
"sentAt": "2026-09-01T19:01:02.627Z",
"deliveredAt": "2026-09-01T19:01:04.133Z",
"failedAt": null
}
A message you did not send with this key returns 404.
List messages
GET /messages
Your key's messages, newest first.
| Query | Description |
|---|---|
| status | Filter by state, e.g. failed |
| limit | 1โ200, default 50 |
| before | Pass the previous response's nextBefore to page back |
curl "https://smsgateway.swiftcoder.in/api/v1/messages?status=failed&limit=20" \ -H "Authorization: Bearer $SMS_API_KEY"
{
"data": [ { "id": "6a97โฆ", "to": "+919140327455", "status": "failed" } ],
"nextBefore": "6a97โฆ"
}
nextBefore is null on the last page. Paging is
by cursor, so new messages arriving mid-scan will not shift your results.
Delivery states
A message moves forward through these states and never backwards.
| State | Meaning |
|---|---|
| queued | Accepted and waiting for capacity. |
| dispatched | Being sent right now. |
| sent | Handed to the mobile network. |
| delivered | The network confirmed it reached the handset. |
| failed | Terminal, after 3 attempts. lastError says why. |
sent as success. Delivery receipts are optional
for carriers and many never send one, so a delivered message can sit at
sent forever. Waiting for delivered before you
act will strand a large share of perfectly good traffic.
Failures are retried automatically up to 3 times before a
message is marked failed; a send that goes unconfirmed for
120 seconds is returned to the queue and tried
again. You do not need to implement retries yourself.
Idempotency
Network timeouts leave you unsure whether a message was accepted.
Resending blindly risks a duplicate. Send a clientRef โ any
string unique to that message in your own system โ and a repeat of the
same request returns the original message instead of sending a second one.
{ "to": "+919140327455", "body": "Your code is 123456", "clientRef": "signup-8842" }
Scoped to your API key, so your references cannot collide with anyone else's. The response is the first message โ including its original body โ so a changed body under a reused reference is not sent.
Errors
Failures return the matching HTTP status and a JSON body with a stable
error code and a human-readable message. Branch
on the code, not the text.
{ "error": "invalid_recipient", "message": "`to` must be a phone number, ideally in E.164 form (+15551234567)." }
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_recipient | Missing or unparseable to |
| 400 | invalid_body | Missing or empty body |
| 400 | invalid_channel | Unknown channel requested |
| 400 | too_many_messages | More than 500 in one bulk request |
| 401 | missing_api_key | No key on the request |
| 401 | invalid_api_key | Unknown or revoked key |
| 403 | insufficient_scope | Key lacks the required scope |
| 404 | not_found | No such message under this key |
A 4xx will not succeed on retry โ fix the request. A
5xx is safe to retry, and safest with a
clientRef.
Health
GET /health
Unauthenticated liveness check, suitable for a monitor.
curl https://smsgateway.swiftcoder.in/api/v1/health
{ "ok": true, "uptime": 1284.51 }