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/accounts
  • POST /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 tierBurst capacityToken refill
Starter1 message1 token every 1,200 ms
Growth5 messages1 token every 1,200 ms
Scale10 messages1 token every 1,200 ms
Enterprise20 messages1 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 2xx quickly.
  • Use message logs and request IDs to debug throttled sends.
  • Ask support for higher production throughput after sender registration is stable.