Documentation
Rate Limits
Understand API rate limits, send throughput, retries, and live-mode controls.
Rate limits protect provider reliability, spend controls, and customer experience.
TextTree surfaces rate-limit failures with:
- request ID
- endpoint
- retry guidance
- related message or webhook event
Production throughput depends on sender registration, provider limits, and workspace plan.
Account bootstrap throttle
Headless account bootstrap is intentionally stricter than normal API traffic:
POST /api/v1/accountsPOST /mcp/accounts
Each IP address can create one account successfully every five minutes. TextTree stores only a hash of the IP address for this throttle record. A repeated successful creation attempt within the window returns:
{
"error": "account_creation_rate_limited",
"retry_after_seconds": 300
}
Message send rate limit
POST /api/v1/messages is rate limited per API credential (per access token) so
that one key cannot flood the shared send queue. The limit is a token bucket:
short bursts are allowed, then requests are throttled to a sustained rate.
| Plan tier | Burst capacity | Token refill |
|---|---|---|
| Starter | 1 message | 1 token every 1,200 ms |
| Growth | 5 messages | 1 token every 1,200 ms |
| Scale | 10 messages | 1 token every 1,200 ms |
| Enterprise | 20 messages | 1 token every 1,200 ms |
All tiers keep the default sustained API-admission refill at or below the default Dial provider ceiling of one token every 1,200 ms. Paid tiers increase short-burst admission; actual delivery still queues behind provider registration, Dial rate limits, spend controls, and workspace sender configuration.
When the bucket is empty the endpoint responds with 429 Too Many Requests, a
Retry-After header (in seconds), and:
{
"error": "message_send_rate_limited",
"message": "Too many message send requests. Retry after the indicated delay.",
"retry_after_seconds": 1,
"limit": {
"tier": "starter",
"capacity": 1,
"refill_ms": 1200
}
}
Reads (GET /api/v1/messages/:id) and previews are not affected by this limit.
Higher sustained throughput is available per workspace plan after sender
registration is stable — contact support.
Response shape
{
"error": "message_send_rate_limited",
"message": "Too many message send requests. Retry after the indicated delay.",
"retry_after_seconds": 30,
"limit": {
"tier": "growth",
"capacity": 5,
"refill_ms": 1200
}
}
Use retry_after_seconds when present. If the field is missing, use exponential backoff with
jitter instead of retrying immediately.
Retry pattern
async function sendWithBackoff(payload, attempt = 1) {
const response = await fetch("https://api.texttree.ai/api/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TEXTREE_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (response.status !== 429) {
return response;
}
const retryAfter = Number(response.headers.get("retry-after") || 2 ** attempt);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return sendWithBackoff(payload, attempt + 1);
}
Idempotency
Always include idempotency_key when retrying workflow sends. This lets TextTree return the
original message instead of creating duplicate SMS records when the first request succeeded but
your client timed out.
{
"phone_number": "+15551234567",
"body": "Your appointment is tomorrow at 9 AM.",
"idempotency_key": "appointment-123-reminder"
}
Operational guidance
- Burst tests in Test mode before moving traffic to Live.
- Keep webhook receivers fast and return
2xxquickly. - Use message logs and request IDs to debug throttled sends.
- Ask support for higher production throughput after sender registration is stable.