The Texting Blue API uses conventional HTTP status codes to indicate the success or failure of a request. Codes in the 2xx range indicate success. Codes in the 4xx range indicate a client error. Codes in the 5xx range indicate a server error.
All error responses follow the same JSON structure:
{
"error": {
"code": "invalid_request",
"message": "The 'to' field must be a valid E.164 phone number.",
"param": "to"
}
}| Field | Type | Description |
|---|---|---|
code |
string | A machine-readable error code. |
message |
string | A human-readable description of the error. |
param |
string or null | The request parameter that caused the error, if applicable. |
| HTTP Status | Code | Description |
|---|---|---|
| 400 | invalid_request |
Malformed request or invalid parameters. |
| 401 | unauthorized |
Missing or invalid API key. |
| 402 | plan_limit_exceeded |
Monthly message limit reached. Upgrade your plan. |
| 403 | forbidden |
Insufficient permissions for this action. |
| 404 | not_found |
The requested resource does not exist. |
| 409 | conflict |
The resource already exists (e.g., duplicate webhook URL). |
| 429 | rate_limited |
Too many requests. Check the Retry-After header. |
| 500 | internal_error |
An unexpected server error occurred. |
Returned when the request body is malformed or a parameter has an invalid value.
Invalid phone number format:
{
"error": {
"code": "invalid_request",
"message": "The 'to' field must be a valid E.164 phone number.",
"param": "to"
}
}Missing required field:
{
"error": {
"code": "invalid_request",
"message": "The 'content' field is required.",
"param": "content"
}
}Content exceeds maximum length:
{
"error": {
"code": "invalid_request",
"message": "The 'content' field must not exceed 5000 characters.",
"param": "content"
}
}Invalid media URL:
{
"error": {
"code": "invalid_request",
"message": "The 'media_url' field must be a valid HTTPS URL.",
"param": "media_url"
}
}Returned when the x-api-key header is missing, the key is invalid, or the key has been revoked.
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing API key."
}
}Returned when you have reached your plan's monthly message limit.
{
"error": {
"code": "plan_limit_exceeded",
"message": "You have reached your monthly limit of 1,000 messages. Upgrade your plan to send more."
}
}Returned when your API key does not have the required permission for the endpoint.
{
"error": {
"code": "forbidden",
"message": "Your API key does not have the 'messages:send' permission.",
"param": null
}
}Returned when the requested resource does not exist.
{
"error": {
"code": "not_found",
"message": "Message 'msg_xxxxxxxxxxxx' not found."
}
}Returned when a resource already exists and cannot be duplicated.
{
"error": {
"code": "conflict",
"message": "A webhook with the URL 'https://example.com/webhook' already exists."
}
}Returned when you have exceeded the rate limit for your plan. The response includes a Retry-After header with the number of seconds to wait.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Please retry after 12 seconds."
}
}Response headers:
Retry-After: 12
Returned when an unexpected server error occurs. If you consistently receive this error, contact support.
{
"error": {
"code": "internal_error",
"message": "An unexpected error occurred. Please try again later."
}
}response=$(curl -s -w "\n%{http_code}" -X POST https://api.texting.blue/v1/messages/send \
-H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "Content-Type: application/json" \
-d '{ "to": "invalid", "from": "+14155559876", "content": "Hello" }')
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -ge 400 ]; then
echo "Error ($http_code): $body"
ficonst response = await fetch("https://api.texting.blue/v1/messages/send", {
method: "POST",
headers: {
"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "+14155551234",
from: "+14155559876",
content: "Hello!",
}),
});
if (!response.ok) {
const { error } = await response.json();
console.error(`Error ${response.status}: [${error.code}] ${error.message}`);
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After");
console.log(`Retry after ${retryAfter} seconds`);
}
return;
}
const message = await response.json();import requests
response = requests.post(
"https://api.texting.blue/v1/messages/send",
headers={
"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"Content-Type": "application/json",
},
json={
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello!",
},
)
if not response.ok:
error = response.json()["error"]
print(f"Error {response.status_code}: [{error['code']}] {error['message']}")
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
print(f"Retry after {retry_after} seconds")
else:
message = response.json()$ch = curl_init("https://api.texting.blue/v1/messages/send");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"to" => "+14155551234",
"from" => "+14155559876",
"content" => "Hello!",
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$body = substr($response, $headerSize);
curl_close($ch);
if ($httpCode >= 400) {
$error = json_decode($body, true)["error"];
echo "Error {$httpCode}: [{$error['code']}] {$error['message']}\n";
} else {
$message = json_decode($body, true);
}