Bring SMS
into your service.
Start with one API key. Choose a destination and send to up to 1,000 recipients per request. Track each delivery.
Quick start
- Create an API key
After signup, approval, and OTP login, visitAPI key managementto create a key.
- Add credits
The API uses your account balance and pricing. Credits are charged when your request is queued.
- Send and track your messages
Save the campaign ID and use the status endpoint to track results.
https://hexsms.comCall the API from your server over HTTPS. Preview mode does not issue real keys or send messages.
Coverage
Selected network destinations
- United States
- Canada
- United Kingdom
- Germany
- France
- Spain
- Italy
- Netherlands
- Sweden
- Norway
- Denmark
- Poland
- Romania
- Czechia
- Austria
- Switzerland
- Ireland
- Portugal
- Finland
- Japan
- South Korea
- China
- Taiwan
- Hong Kong SAR China
- Singapore
- Malaysia
- Indonesia
- Philippines
- Thailand
- Vietnam
- India
- Bangladesh
- Pakistan
- Australia
- New Zealand
- Brazil
- Mexico
- South Africa
- Nigeria
- United Arab Emirates
- Saudi Arabia
Destination availability, rates, and sender registration requirements depend on your account and local networks. Check country pricing before sending.
GET/api/v1/messages/countries
Use this endpoint to check enabled destinations and your applied prices. Specify country when sending and use numbers from the same country in each request. Destinations without configured pricing are not accepted.
{ "currency": "USDT", "countries": [{ "country": "US", "unitPrice": 0.02 }] }Authenticate with an API key
Include this header with every API request. Session cookies and OTP codes are not required.
Authorization: Bearer YOUR_API_KEYThe secret is shown only once. Store it in a server environment variable, never in frontend code. Revoked keys and suspended accounts cannot use the API. You can have up to five active keys.
/api/v1/messagesSend to up to 1,000 recipients
Send the same message to multiple recipients. We validate and queue the request before returning a response. For different message content, send separate requests with unique Idempotency-Key values.
| Field | Type | Description |
|---|---|---|
country * | string | ISO 3166-1 alpha-2 country code, such as US, GB, JP, or KR. There is no default country. |
text * | string | 1–1,600 characters after trimming. All recipients in a request receive the same content. |
recipients * | string[] | 1–1,000 mobile numbers. E.164 format with + and the calling code is recommended. All numbers must match country. |
Idempotency-Key * | header | 8–100 ASCII letters, digits, hyphens, or underscores. Choose a unique value for each operation. |
Any invalid or duplicate number rejects the entire request without sending or charging. Duplicates are checked after normalization to international format.
curl -X POST 'https://hexsms.com/api/v1/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: notice-20261024-001' \
-d '{
"country": "US",
"text": "Your verification code is 482913.",
"recipients": ["+12025550123", "+12025550124"]
}'{
"id": "35bed356-b84a-452a-8e94-98b538972ecd",
"country": "US",
"state": "RUNNING",
"total": 2,
"segments": 1,
"unitPrice": 0.025,
"cost": 0.05,
"createdAt": 1792800000000,
"note": null,
"replayed": false
}Retrying with the same account, Idempotency-Key, content, and recipient order returns the original campaign without another send or charge (200, replayed: true). Different content returns 409. Idempotency survives API key rotation. Request keys do not expire.
How pricing works
Recipients × segments × account unit priceUCS-2 supports 70 characters in one segment, then 67 per segment. Basic GSM-7 supports 160, then 153 per segment. Extension characters count twice; emoji use UTF-16 length. unitPrice and cost are in USDT. The response contains the applied price; 0.025 above is only an example.
/api/v1/messages/{id}Track each recipient
Use the id returned by the send endpoint. Only API campaigns owned by the same account are accessible. This endpoint returns stored results. Call refresh first if you need the latest carrier results.
curl 'https://hexsms.com/api/v1/messages/35bed356-b84a-452a-8e94-98b538972ecd?limit=1&offset=0' \
-H 'Authorization: Bearer YOUR_API_KEY'{
"id": "35bed356-b84a-452a-8e94-98b538972ecd",
"country": "US",
"state": "DONE",
"total": 2, "segments": 1,
"unitPrice": 0.025, "cost": 0.05,
"createdAt": 1792800000000, "note": null,
"summary": { "DELIVERED": 1, "PENDING": 1 },
"messages": [
{
"id": "d2787184-d8b8-49c6-9c15-1db90a6d5516",
"to": "12025550123",
"status": "DELIVERED",
"statusName": "DELIVERED_TO_HANDSET",
"description": "Message delivered to handset",
"error": null,
"updatedAt": 1792800005000
}
],
"pagination": { "limit": 1, "offset": 0, "nextOffset": 1 }
}limit is 1–1,000 (default 100); offset is 0–1,000 (default 0). A null nextOffset means the last page. summary counts all recipients by status. to uses international digits without +. Timestamps are Unix milliseconds. Results may differ from input order; identify each message by its id.
It means queue processing is complete. Check each message for status DELIVERED to confirm delivery.
/api/v1/messages/{id}/refreshFetch the latest delivery results
Refresh stored results from carrier logs. No request body is needed. Read results with GET afterward. Refresh a campaign at most once every 30 seconds. Carrier logs are available for 48 hours after sending. Configured delivery webhooks may update results automatically.
curl -X POST 'https://hexsms.com/api/v1/messages/35bed356-b84a-452a-8e94-98b538972ecd/refresh' \
-H 'Authorization: Bearer YOUR_API_KEY'{ "id": "35bed356-b84a-452a-8e94-98b538972ecd", "updated": 2 }updated is the number of rows updated. It can be zero while messages are queued or no new results exist. Poll GET at intervals of at least 10 seconds and call refresh only when needed.
Understand status codes
Campaign state
PREPARING: preparing · RUNNING: processing · PAUSED: paused · DONE: queue processed · CANCELED: canceled
| Per-recipient status | Meaning |
|---|---|
| QUEUED | Waiting in the queue. |
| SENDING | Submitting to the carrier. |
| PENDING | Accepted by the carrier; awaiting the final result. |
| DELIVERED | Delivered to the recipient device. |
| UNDELIVERABLE | Could not be delivered. |
| EXPIRED | The delivery validity period expired. |
| REJECTED | Rejected or a processing error occurred. |
| CANCELED | Canceled before sending. |
| UNKNOWN | The result is not yet known. |
Handle unknown status values, as carriers may add new codes. Check statusName, description, and error for failure details.
Handling errors
{ "error": "Insufficient credits. Please top up and retry.", "code": "INSUFFICIENT_CREDITS" }| HTTP | code | What to check |
|---|---|---|
| 403 | COUNTRY_NOT_ENABLED | Check country pricing and activation. |
| 400 | INVALID_COUNTRY | Set country to an ISO country code. |
| 400 | INVALID_BODY / INVALID_JSON / INVALID_TEXT | Check the JSON object and message content. |
| 400 | INVALID_RECIPIENTS / INVALID_PHONE / DUPLICATE_PHONE | Check recipient count, format, and duplicates. |
| 400 | INVALID_IDEMPOTENCY_KEY / INVALID_PAGINATION | Check the idempotency key or pagination values. |
| 401 | UNAUTHORIZED | The Bearer API key is missing or invalid. |
| 402 | INSUFFICIENT_CREDITS | Top up your credits. |
| 403 | ACCOUNT_INACTIVE | Make sure the account is approved and active. |
| 404 | NOT_FOUND | Check the campaign ID and ownership. |
| 409 | IDEMPOTENCY_CONFLICT | The request key was reused with different content. |
| 413 / 415 | BODY_TOO_LARGE / UNSUPPORTED_MEDIA_TYPE | Send a JSON body no larger than 128KiB. |
| 429 | RATE_LIMITED / REFRESH_TOO_SOON | Wait the seconds specified in Retry-After before retrying. |
| 500 / 502 | INTERNAL_ERROR / PROVIDER_UNAVAILABLE | Try again later. For send retries, use the same request key. |
| 503 | PREVIEW_ONLY | Real API calls are disabled in preview mode. |
Each API key allows 120 requests per minute across sending, status, and refresh. Check Retry-After when limited. This API is for server-to-server use; browser CORS calls are not supported.
Connect with Node.js
const response = await fetch('https://hexsms.com/api/v1/messages', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.HEXSMS_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'notice-20261024-001'
},
body: JSON.stringify({
country: 'US',
text: 'Your order is on its way.',
recipients: ['+12025550123', '+12025550124']
})
});
const result = await response.json();
if (!response.ok) throw new Error(result.code + ': ' + result.error);
console.log(result.id); // Use this ID to check delivery status.Create a request key once per operation and reuse it for retries. Resending a successful operation with a new key creates another send and charge.