Send and retrieve iMessages through the Texting Blue API. Messages are delivered through your connected Apple device and support text, media, and group conversations.
Every message progresses through the following statuses:
queued → polling → sent → delivered
↘ failed
| Status | Description |
|---|---|
queued |
Message accepted and waiting to be picked up by your device. |
polling |
Your device has received the message and is attempting to send it. |
sent |
Message was handed off to Apple's iMessage servers. |
delivered |
Recipient's device confirmed delivery. |
failed |
Message could not be delivered. See error_code and error_message. |
POST /v1/messages/send
Permission: messages:send
Queues a message for delivery through your connected device. Returns immediately with a 202 Accepted status.
| Field | Type | Required | Description |
|---|---|---|---|
to |
string | Yes | Recipient phone number in E.164 format (e.g., +14155551234). |
from |
string | Yes | Your connected phone number in E.164 format. |
content |
string | Yes | Message body. Maximum 5,000 characters. |
media_url |
string | No | HTTPS URL of a media file to attach. Maximum 5 MB. |
{
"id": "msg_xxxxxxxxxxxx",
"status": "queued",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello from Texting Blue!",
"media_url": null,
"created_at": "2026-02-07T12:00:00Z"
}curl -X POST https://api.texting.blue/v1/messages/send \
-H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello from Texting Blue!"
}'const 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 from Texting Blue!",
}),
});
const message = await response.json();
console.log(message.id); // "msg_xxxxxxxxxxxx"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 from Texting Blue!",
},
)
message = response.json()
print(message["id"]) # "msg_xxxxxxxxxxxx"$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 from Texting Blue!",
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$message = json_decode($response, true);
echo $message["id"]; // "msg_xxxxxxxxxxxx"Attach an image or file by providing a publicly accessible HTTPS URL:
curl -X POST https://api.texting.blue/v1/messages/send \
-H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234",
"from": "+14155559876",
"content": "Check out this photo!",
"media_url": "https://example.com/photo.jpg"
}'GET /v1/messages
Permission: messages:read
Retrieve a paginated list of messages. Use query parameters to filter results.
| Parameter | Type | Default | Description |
|---|---|---|---|
from |
string | -- | Filter by sender phone number (E.164). |
to |
string | -- | Filter by recipient phone number (E.164). |
status |
string | -- | Filter by status: queued, polling, sent, delivered, failed. |
direction |
string | -- | Filter by direction: inbound or outbound. |
limit |
integer | 50 | Number of messages to return (1--100). |
cursor |
string | -- | Pagination cursor from a previous response. |
{
"messages": [
{
"id": "msg_xxxxxxxxxxxx",
"status": "delivered",
"direction": "outbound",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello from Texting Blue!",
"media_url": null,
"created_at": "2026-02-07T12:00:00Z",
"delivered_at": "2026-02-07T12:00:03Z"
}
],
"cursor": "msg_yyy",
"has_more": true
}curl -X GET "https://api.texting.blue/v1/messages?from=%2B14155559876&limit=10" \
-H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"const params = new URLSearchParams({
from: "+14155559876",
limit: "10",
});
const response = await fetch(
`https://api.texting.blue/v1/messages?${params}`,
{
headers: {
"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
},
}
);
const { messages, cursor, has_more } = await response.json();import requests
response = requests.get(
"https://api.texting.blue/v1/messages",
headers={"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
params={"from": "+14155559876", "limit": 10},
)
data = response.json()
messages = data["messages"]$query = http_build_query([
"from" => "+14155559876",
"limit" => 10,
]);
$ch = curl_init("https://api.texting.blue/v1/messages?" . $query);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$messages = $data["messages"];When has_more is true, pass the cursor value to fetch the next page:
curl -X GET "https://api.texting.blue/v1/messages?cursor=msg_yyy&limit=50" \
-H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"GET /v1/messages/:id
Permission: messages:read
Retrieve the full details of a single message, including error information for failed messages.
{
"id": "msg_xxxxxxxxxxxx",
"status": "delivered",
"direction": "outbound",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello from Texting Blue!",
"media_url": null,
"error_code": null,
"error_message": null,
"created_at": "2026-02-07T12:00:00Z",
"delivered_at": "2026-02-07T12:00:03Z"
}For failed messages:
{
"id": "msg_xxxxxxxxxxxx",
"status": "failed",
"direction": "outbound",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello!",
"media_url": null,
"error_code": "device_offline",
"error_message": "The sending device is not connected.",
"created_at": "2026-02-07T12:00:00Z",
"delivered_at": null
}curl -X GET https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx \
-H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"const response = await fetch(
"https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx",
{
headers: {
"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
},
}
);
const message = await response.json();
console.log(message.status); // "delivered"import requests
response = requests.get(
"https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx",
headers={"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
)
message = response.json()
print(message["status"]) # "delivered"$ch = curl_init("https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$message = json_decode($response, true);
echo $message["status"]; // "delivered"| HTTP Status | Code | Description |
|---|---|---|
| 400 | invalid_request |
Malformed request body or invalid parameters (e.g., bad phone number format). |
| 401 | unauthorized |
Missing or invalid API key. |
| 402 | plan_limit_exceeded |
Your monthly message limit has been reached. Upgrade your plan. |
| 403 | forbidden |
Your API key does not have the required permission. |
| 429 | rate_limited |
Too many requests. Check the Retry-After header. |