Messages
Send iMessages, list message history, and retrieve individual messages.
Send a message
POST /v1/messages/send
Send an iMessage from your connected iPhone number.
Headers
| Header | Required | Description |
|---|---|---|
x-api-key | Yes | Your API key |
Content-Type | Yes | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Recipient phone number (E.164 format, e.g., +14155551234) |
from | string | Yes | Your connected phone number (E.164 format) |
content | string | Yes | Message text (max 5,000 characters) |
media_url | string | No | URL of media to attach (must be HTTPS, max 5MB). This functionality is working only with Standard plan and higher. |
Example
- cURL
- JavaScript
- Python
curl -X POST https://api.texting.blue/v1/messages/send \
-H "Content-Type: application/json" \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d '{
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello from Texting Blue!"
}'
const response = await fetch('https://api.texting.blue/v1/messages/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
},
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={
'Content-Type': 'application/json',
'x-api-key': 'tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
},
json={
'to': '+14155551234',
'from': '+14155559876',
'content': 'Hello from Texting Blue!',
},
)
message = response.json()
print(message['id']) # msg_xxxxxxxxxxxx
Response
Status: 202 Accepted
{
"id": "msg_xxxxxxxxxxxx",
"status": "queued",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello from Texting Blue!",
"media_url": null,
"created_at": "2026-02-07T12:00:00Z"
}
Message status lifecycle
Messages go through these statuses:
- queued -- Message created, waiting for your iPhone to pick it up
- polling -- Your iPhone shortcut has received the message
- sent -- iMessage sent successfully from your iPhone
- delivered -- Delivery confirmed (when available)
- failed -- Message could not be sent
Errors
| Status | Code | Description |
|---|---|---|
| 400 | invalid_request | Invalid phone number format or missing fields |
| 401 | unauthorized | Invalid API key |
| 402 | plan_limit_exceeded | Monthly message limit reached |
| 403 | forbidden | API key lacks messages:send permission |
| 429 | rate_limited | Too many requests |
List messages
GET /v1/messages
Retrieve a paginated list of messages for your team.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
from | string | -- | Filter by sender phone number |
to | string | -- | Filter by recipient phone number |
status | string | -- | Filter by status (queued, sent, delivered, failed, received) |
direction | string | -- | Filter by direction (inbound, outbound) |
limit | integer | 50 | Number of messages to return (1-100) |
cursor | string | -- | Cursor for pagination (message ID from previous response) |
Example
- cURL
- JavaScript
- Python
curl "https://api.texting.blue/v1/messages?from=%2B14155559876&limit=10" \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
},
});
const data = await response.json();
console.log(data.messages);
import requests
response = requests.get(
'https://api.texting.blue/v1/messages',
headers={'x-api-key': 'tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'},
params={'from': '+14155559876', 'limit': 10},
)
data = response.json()
for msg in data['messages']:
print(f"{msg['direction']} {msg['status']}: {msg['content'][:50]}")
Response
Status: 200 OK
{
"messages": [
{
"id": "msg_xxxxxxxxxxxx",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello!",
"media_url": null,
"status": "delivered",
"direction": "outbound",
"created_at": "2026-02-07T12:00:00Z",
"delivered_at": "2026-02-07T12:00:03Z"
}
],
"cursor": "msg_yyyyyyyyyyyy",
"has_more": true
}
Pagination
Results are returned in reverse chronological order. To fetch the next page, pass the cursor value from the response as the cursor query parameter:
curl "https://api.texting.blue/v1/messages?cursor=msg_yyyyyyyyyyyy&limit=10" \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
When has_more is false, you've reached the end of the results.
Get a message
GET /v1/messages/:id
Retrieve a single message by its ID.
Example
- cURL
- JavaScript
- Python
curl "https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx" \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
const response = await fetch('https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx', {
headers: {
'x-api-key': 'tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
},
});
const message = await response.json();
console.log(message.status);
import requests
response = requests.get(
'https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx',
headers={'x-api-key': 'tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'},
)
message = response.json()
print(message['status'])
Response
Status: 200 OK
{
"id": "msg_xxxxxxxxxxxx",
"to": "+14155551234",
"from": "+14155559876",
"content": "Hello!",
"media_url": null,
"status": "delivered",
"direction": "outbound",
"error_code": null,
"error_message": null,
"created_at": "2026-02-07T12:00:00Z",
"delivered_at": "2026-02-07T12:00:03Z"
}
Next steps
- Phone numbers -- Manage connected numbers
- Webhooks -- Get notified about message events
- Error codes -- Understand error responses