Skip to content

Latest commit

 

History

History
229 lines (179 loc) · 6.3 KB

File metadata and controls

229 lines (179 loc) · 6.3 KB

Rate Limits

Texting Blue enforces rate limits to ensure fair usage and platform stability. Limits vary by plan and are applied per account.

Plan Limits

Scope Free Starter Pro Enterprise
API requests/min 60 300 1,000 5,000
Messages/min 5 20 60 200
Monthly messages 100 1,000 10,000 Custom
Phone numbers 1 2 5 Unlimited
Team members 1 3 10 Unlimited

Rate Limit Headers

Every API response includes a header indicating your remaining request budget for the current window:

Header Description
x-ratelimit-remaining Number of requests remaining in the current minute window.

When you exceed the rate limit, the API returns a 429 Too Many Requests response with an additional header:

Header Description
Retry-After Number of seconds to wait before making another request.

Example 429 Response

HTTP/1.1 429 Too Many Requests
Retry-After: 12
x-ratelimit-remaining: 0
Content-Type: application/json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please retry after 12 seconds."
  }
}

Best Practices

Implement Exponential Backoff

When you receive a 429 response, wait the number of seconds indicated by Retry-After before retrying. For repeated failures, use exponential backoff to progressively increase the delay.

Node.js

async function sendWithRetry(payload, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(
      "https://api.texting.blue/v1/messages/send",
      {
        method: "POST",
        headers: {
          "x-api-key": process.env.TEXTINGBLUE_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      }
    );

    if (response.status !== 429) {
      return response.json();
    }

    const retryAfter = parseInt(response.headers.get("Retry-After") || "1", 10);
    const delay = retryAfter * 1000 * Math.pow(2, attempt);
    console.log(`Rate limited. Retrying in ${delay / 1000}s...`);
    await new Promise((resolve) => setTimeout(resolve, delay));
  }

  throw new Error("Max retries exceeded");
}

Python

import os
import time

import requests


def send_with_retry(payload: dict, max_retries: int = 3) -> dict:
    for attempt in range(max_retries + 1):
        response = requests.post(
            "https://api.texting.blue/v1/messages/send",
            headers={
                "x-api-key": os.environ["TEXTINGBLUE_API_KEY"],
                "Content-Type": "application/json",
            },
            json=payload,
        )

        if response.status_code != 429:
            return response.json()

        retry_after = int(response.headers.get("Retry-After", "1"))
        delay = retry_after * (2**attempt)
        print(f"Rate limited. Retrying in {delay}s...")
        time.sleep(delay)

    raise Exception("Max retries exceeded")

PHP

function sendWithRetry(array $payload, int $maxRetries = 3): array
{
    for ($attempt = 0; $attempt <= $maxRetries; $attempt++) {
        $ch = curl_init("https://api.texting.blue/v1/messages/send");
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            "x-api-key: " . getenv("TEXTINGBLUE_API_KEY"),
            "Content-Type: application/json",
        ]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        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);
        $headers = substr($response, 0, $headerSize);
        curl_close($ch);

        if ($httpCode !== 429) {
            return json_decode($body, true);
        }

        preg_match('/Retry-After:\s*(\d+)/i', $headers, $matches);
        $retryAfter = isset($matches[1]) ? (int) $matches[1] : 1;
        $delay = $retryAfter * pow(2, $attempt);
        echo "Rate limited. Retrying in {$delay}s...\n";
        sleep($delay);
    }

    throw new \RuntimeException("Max retries exceeded");
}

Use Client-Side Queuing

If your application sends messages in bulk, queue them locally and send at a steady rate below your plan's limit. This avoids hitting rate limits entirely.

class MessageQueue {
  constructor(apiKey, messagesPerMinute) {
    this.apiKey = apiKey;
    this.interval = (60 / messagesPerMinute) * 1000; // ms between sends
    this.queue = [];
    this.processing = false;
  }

  enqueue(payload) {
    this.queue.push(payload);
    if (!this.processing) {
      this.process();
    }
  }

  async process() {
    this.processing = true;

    while (this.queue.length > 0) {
      const payload = this.queue.shift();

      await fetch("https://api.texting.blue/v1/messages/send", {
        method: "POST",
        headers: {
          "x-api-key": this.apiKey,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      });

      // Wait to stay under rate limit
      await new Promise((resolve) => setTimeout(resolve, this.interval));
    }

    this.processing = false;
  }
}

// Starter plan: 20 messages/min
const queue = new MessageQueue("tb_live_...", 20);

queue.enqueue({ to: "+14155551234", from: "+14155559876", content: "Message 1" });
queue.enqueue({ to: "+14155551235", from: "+14155559876", content: "Message 2" });
queue.enqueue({ to: "+14155551236", from: "+14155559876", content: "Message 3" });

Monitor Remaining Requests

Check the x-ratelimit-remaining header on every response to proactively slow down before hitting the limit:

const response = await fetch("https://api.texting.blue/v1/messages/send", {
  method: "POST",
  headers: {
    "x-api-key": process.env.TEXTINGBLUE_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

const remaining = parseInt(
  response.headers.get("x-ratelimit-remaining") || "0",
  10
);

if (remaining < 10) {
  console.warn(`Only ${remaining} requests remaining in this window`);
  // Slow down or pause sending
}