Rate Limits#

API rate limits, usage-based pricing, and platform-specific quotas.

API Rate Limits#

Rate limits are set per API key. Every key defaults to 100 requests/minute. You can set a custom rate limit (between 1 and 1,000 requests/minute) when creating or updating a key in the console under Settings → API Keys.

If you need more sustained throughput than a single key allows, contact us.

Pricing#

Pricing is usage-based, and there are no plans or tiers:

MetricPrice
API calls$0.01 per call
Storage$1.00 per GB/month

Every account receives a $10 free usage credit each month (roughly 1,000 API calls), effectively a free tier for development and small projects. For volume pricing or SLAs, contact us.

Rate Limit Headers#

Every response to an API-key request includes rate limit headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1705318260
HeaderDescription
X-RateLimit-LimitMax requests per minute for this key
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

Rate Limit Response#

When rate limited:

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{
  "error": "Rate limit exceeded",
  "limit": 100,
  "window": "1 minute"
}

Platform Rate Limits#

Each messaging platform has its own rate limits:

WhatsApp Business#

TierMessages/DayHow to Upgrade
Tier 11,000Start here
Tier 210,000Good quality
Tier 3100,000Sustained quality
Tier 4UnlimitedHigh volume

Quality Score: Affects tier progression

  • User responses
  • Block rate
  • Spam reports

Instagram#

ScopeLimit
Per user250 messages/24 hours
API calls200/user/hour

Facebook Messenger#

ScopeLimit
Per recipient250 messages/24 hours
API calls200/user/hour
Batch1000 messages/batch

Telegram#

ScopeLimit
Private chats~30 messages/second
GroupsVaries by size
Bot API30 requests/second
Bulk notifications25-30/second

Slack#

ScopeLimit
Web API1 request/second (tier 1-4)
Posting messages1/channel/second
Events APINo explicit limit

Discord#

ScopeLimit
Per channel5 messages/5 seconds
Per user DM5 messages/5 seconds
GlobalVaries by endpoint

SMS (Twilio)#

Number TypeLimit
Long code1 message/second
Toll-free3 messages/second
Short code100 messages/second

Handling Rate Limits#

Exponential Backoff#

import time
import random

def send_with_backoff(send_func, max_retries=5):
    for attempt in range(max_retries):
        try:
            return send_func()
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise

            # Exponential backoff with jitter
            delay = (2 ** attempt) + random.uniform(0, 1)
            time.sleep(delay)

Request Queuing#

For high-volume messaging:

import asyncio
from collections import deque

class RateLimitedQueue:
    def __init__(self, rate_per_second: float = 1.0):
        self.queue = deque()
        self.rate = rate_per_second
        self.last_request = 0

    async def enqueue(self, request):
        self.queue.append(request)
        await self._process()

    async def _process(self):
        while self.queue:
            now = time.time()
            wait_time = (1 / self.rate) - (now - self.last_request)

            if wait_time > 0:
                await asyncio.sleep(wait_time)

            request = self.queue.popleft()
            self.last_request = time.time()

            try:
                await request.execute()
            except RateLimitError:
                self.queue.appendleft(request)
                await asyncio.sleep(60)  # Wait for reset

Platform-Specific Handling#

class PlatformRateLimiter:
    LIMITS = {
        "whatsapp": {"per_second": 80, "daily": 1000},
        "telegram": {"per_second": 30},
        "slack": {"per_second": 1},
        "sms": {"per_second": 1},
    }

    def __init__(self, platform: str):
        self.limits = self.LIMITS.get(platform, {})
        self.window_start = time.time()
        self.request_count = 0

    def can_send(self) -> bool:
        now = time.time()

        # Reset window if needed
        if now - self.window_start >= 1:
            self.window_start = now
            self.request_count = 0

        # Check rate
        per_second = self.limits.get("per_second", float("inf"))
        return self.request_count < per_second

    def record_request(self):
        self.request_count += 1

Best Practices#

1. Respect Limits#

Always check rate limit headers and wait appropriately.

2. Implement Queuing#

Queue messages during high-volume periods instead of hitting limits.

3. Batch Where Possible#

Combine multiple operations into a single GraphQL request. One request with several fields costs one API call against your rate limit.

4. Monitor Usage#

Track the X-RateLimit-Remaining header to anticipate limit issues, and watch your usage in the console.

5. Plan for Spikes#

Account for traffic spikes in your rate limit strategy. Use separate API keys per service so a burst from one workload doesn’t starve the others.

6. Raise Per-Key Limits When Needed#

Set a higher rateLimitPerMinute on keys that legitimately need it (up to 1,000). For anything beyond that, contact us.

Webhook Rate Limits#

Delivery Rate#

Outeract delivers webhooks as fast as your server accepts them. If your server is slow:

  1. Webhooks queue up
  2. Delivery may be delayed
  3. Eventually, oldest events may be dropped

Recommendation#

Return 200 OK immediately and process asynchronously:

@app.post("/webhook")
async def webhook(request: Request, background_tasks: BackgroundTasks):
    body = await request.json()
    background_tasks.add_task(process_webhook, body)
    return {"received": True}  # Return immediately