curl -X POST https://api.tracklysms.com/api/v2/send-raw/bulk \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+14155551234",
"list_number": "+18005551000",
"body": "Verification code: 482901. Ref: {{messageId}}",
"skip_duplicate_check": true,
"skip_rate_limit": true
},
{
"to": "+14155559876",
"list_number": "+18005551000",
"body": "Verification code: 739204. Ref: {{messageId}}",
"skip_duplicate_check": true,
"skip_rate_limit": true,
"skip_contact_validation": true
}
]
}'
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/send-raw/bulk",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
json={
"messages": [
{
"to": "+14155551234",
"list_number": "+18005551000",
"body": "Verification code: 482901. Ref: {{messageId}}",
"skip_duplicate_check": True,
"skip_rate_limit": True,
},
{
"to": "+14155559876",
"list_number": "+18005551000",
"body": "Verification code: 739204. Ref: {{messageId}}",
"skip_duplicate_check": True,
"skip_rate_limit": True,
"skip_contact_validation": True,
},
]
},
)
data = response.json()
print(f"Queued: {data['queued_count']}, Errors: {data['error_count']}")
const response = await fetch("https://api.tracklysms.com/api/v2/send-raw/bulk", {
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
to: "+14155551234",
list_number: "+18005551000",
body: "Verification code: 482901. Ref: {{messageId}}",
skip_duplicate_check: true,
skip_rate_limit: true,
},
{
to: "+14155559876",
list_number: "+18005551000",
body: "Verification code: 739204. Ref: {{messageId}}",
skip_duplicate_check: true,
skip_rate_limit: true,
skip_contact_validation: true,
},
],
}),
});
const data = await response.json();
console.log(`Queued: ${data.queued_count}, Errors: ${data.error_count}`);
{
"queued_count": 2,
"error_count": 0,
"errors": []
}
{
"queued_count": 1,
"error_count": 1,
"errors": [
{
"index": 1,
"to": "+14155559876",
"code": "list_not_found",
"error": "Sending list not found for this list_number"
}
]
}
{
"error": "messages array is required",
"code": "missing_messages"
}
{
"error": "Maximum 1000 messages per request",
"code": "too_many_messages"
}
Messages (v2)
Send Raw Bulk Messages
Send multiple raw SMS messages in bulk with optional skip flags for advanced control.
POST
/
v2
/
send-raw
/
bulk
curl -X POST https://api.tracklysms.com/api/v2/send-raw/bulk \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+14155551234",
"list_number": "+18005551000",
"body": "Verification code: 482901. Ref: {{messageId}}",
"skip_duplicate_check": true,
"skip_rate_limit": true
},
{
"to": "+14155559876",
"list_number": "+18005551000",
"body": "Verification code: 739204. Ref: {{messageId}}",
"skip_duplicate_check": true,
"skip_rate_limit": true,
"skip_contact_validation": true
}
]
}'
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/send-raw/bulk",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
json={
"messages": [
{
"to": "+14155551234",
"list_number": "+18005551000",
"body": "Verification code: 482901. Ref: {{messageId}}",
"skip_duplicate_check": True,
"skip_rate_limit": True,
},
{
"to": "+14155559876",
"list_number": "+18005551000",
"body": "Verification code: 739204. Ref: {{messageId}}",
"skip_duplicate_check": True,
"skip_rate_limit": True,
"skip_contact_validation": True,
},
]
},
)
data = response.json()
print(f"Queued: {data['queued_count']}, Errors: {data['error_count']}")
const response = await fetch("https://api.tracklysms.com/api/v2/send-raw/bulk", {
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
to: "+14155551234",
list_number: "+18005551000",
body: "Verification code: 482901. Ref: {{messageId}}",
skip_duplicate_check: true,
skip_rate_limit: true,
},
{
to: "+14155559876",
list_number: "+18005551000",
body: "Verification code: 739204. Ref: {{messageId}}",
skip_duplicate_check: true,
skip_rate_limit: true,
skip_contact_validation: true,
},
],
}),
});
const data = await response.json();
console.log(`Queued: ${data.queued_count}, Errors: ${data.error_count}`);
{
"queued_count": 2,
"error_count": 0,
"errors": []
}
{
"queued_count": 1,
"error_count": 1,
"errors": [
{
"index": 1,
"to": "+14155559876",
"code": "list_not_found",
"error": "Sending list not found for this list_number"
}
]
}
{
"error": "messages array is required",
"code": "missing_messages"
}
{
"error": "Maximum 1000 messages per request",
"code": "too_many_messages"
}
Send up to 1,000 raw SMS messages in a single API call. Each message in the batch is validated independently — successfully validated messages are queued even if others in the batch fail. Each message object accepts a set of
Individual messages that fail are reported per-message (below) and the request still returns
skip_* flags intended for granular control over validation and rate-limiting behavior.
The per-message
skip_* flags are currently accepted but have no effect. The endpoint does not read them, so every check runs regardless of what you pass. Until the flags are wired up, this endpoint behaves the same as POST /v2/send/bulk except that it does not perform link wrapping or contact-data macro substitution.The following checks are always enforced:
- Your account must own the sending list
- Phone numbers must be in E.164 format
- Your account must be in active status
Body Parameters
array
required
Array of raw message objects (maximum 1,000 per request). Each object supports the following fields:
Show Message object fields
Show Message object fields
string
required
Recipient phone number in E.164 format (e.g.
+14155551234).string
required
Sending list phone number in E.164 format. Must belong to your account.
string
required
Message body text. Use the
{{messageId}} placeholder to insert the unique message ID into the body at send time.boolean
default:"false"
Intended to skip the duplicate message check. Currently accepted but ignored — this flag has no effect.
boolean
default:"false"
Intended to skip per-list rate limiting. Currently accepted but ignored — this flag has no effect.
boolean
default:"false"
Intended to skip contact validation such as opt-out and block list checks. Currently accepted but ignored — this flag has no effect.
boolean
default:"false"
Intended to skip the journey enrollment check. Currently accepted but ignored — this flag has no effect.
object
Optional key-value metadata dictionary. Currently accepted but not stored or returned — the endpoint discards this field, so it cannot yet be used for correlation.
Response Fields
integer
Number of messages successfully queued for delivery.
integer
Number of messages that failed validation or queueing.
array
Array of error objects for messages that failed validation or queueing. Each object contains:
Show Error object fields
Show Error object fields
integer
Zero-based index of the failed message in the original
messages array.string
The recipient phone number from the failed message, if provided.
string
Machine-readable error code identifying the failure.
string
Human-readable error description.
string
Present only when
code is kafka_producer_failure. Constant value KAFKA_PRODUCER_FAILURE, indicating the message passed validation but could not be published to the delivery queue.string
Present only when
code is kafka_producer_failure. The message ID that was assigned before the publish failure.Examples
curl -X POST https://api.tracklysms.com/api/v2/send-raw/bulk \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+14155551234",
"list_number": "+18005551000",
"body": "Verification code: 482901. Ref: {{messageId}}",
"skip_duplicate_check": true,
"skip_rate_limit": true
},
{
"to": "+14155559876",
"list_number": "+18005551000",
"body": "Verification code: 739204. Ref: {{messageId}}",
"skip_duplicate_check": true,
"skip_rate_limit": true,
"skip_contact_validation": true
}
]
}'
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/send-raw/bulk",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
json={
"messages": [
{
"to": "+14155551234",
"list_number": "+18005551000",
"body": "Verification code: 482901. Ref: {{messageId}}",
"skip_duplicate_check": True,
"skip_rate_limit": True,
},
{
"to": "+14155559876",
"list_number": "+18005551000",
"body": "Verification code: 739204. Ref: {{messageId}}",
"skip_duplicate_check": True,
"skip_rate_limit": True,
"skip_contact_validation": True,
},
]
},
)
data = response.json()
print(f"Queued: {data['queued_count']}, Errors: {data['error_count']}")
const response = await fetch("https://api.tracklysms.com/api/v2/send-raw/bulk", {
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
to: "+14155551234",
list_number: "+18005551000",
body: "Verification code: 482901. Ref: {{messageId}}",
skip_duplicate_check: true,
skip_rate_limit: true,
},
{
to: "+14155559876",
list_number: "+18005551000",
body: "Verification code: 739204. Ref: {{messageId}}",
skip_duplicate_check: true,
skip_rate_limit: true,
skip_contact_validation: true,
},
],
}),
});
const data = await response.json();
console.log(`Queued: ${data.queued_count}, Errors: ${data.error_count}`);
{
"queued_count": 2,
"error_count": 0,
"errors": []
}
{
"queued_count": 1,
"error_count": 1,
"errors": [
{
"index": 1,
"to": "+14155559876",
"code": "list_not_found",
"error": "Sending list not found for this list_number"
}
]
}
{
"error": "messages array is required",
"code": "missing_messages"
}
{
"error": "Maximum 1000 messages per request",
"code": "too_many_messages"
}
Error Codes
Request-Level Errors
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | missing_messages | The messages array is required but was not provided. |
| 413 | too_many_messages | The messages array exceeds the 1,000 message limit. |
201. The request returns 502 only when every message fails to queue for delivery. Authenticated requests can also fail with 401 invalid_credentials, 403 account_suspended, or 429 rate_limited — see Error Codes.
Per-Message Errors
Returned inside each failed entry of the response’serrors[] array (as code), with the request status 201.
| Code | Description |
|---|---|
missing_to | The to field is required but was not provided. |
missing_list_number | The list_number field is required but was not provided. |
missing_body | The body field is required but was not provided. |
invalid_phone | The to field is not a valid E.164 phone number. |
invalid_list_number | The list_number field is not a valid E.164 phone number. |
list_not_found | The sending list was not found or does not belong to your account. |
webhook_not_configured | Webhook verification required before sending (BYOC lists). |
pending_confirmation | The recipient has not confirmed their double opt-in yet, so they cannot be messaged. |
doi_expired | The recipient never confirmed their double opt-in and the confirmation window has expired. |
warmup_limit | The recipient is not yet eligible to be messaged under the list’s warm-up schedule. |
kafka_producer_failure | The message could not be queued for delivery (transient); retry the failed records. |
Next Steps
Campaign Execution
How messages flow from queue to delivery
Create Contact
Add contacts before sending
⌘I