Rate Limits

Complete guide to understanding and handling API rate limits.


Overview

Archivus enforces rate limits to ensure fair usage and system stability. Rate limits vary by subscription tier and endpoint type.


Rate Limit Tiers

Tier Rate Limit Window
Free 15 requests 1 minute
Starter 30 requests 1 minute
Pro 120 requests 1 minute
Team 300 requests 1 minute
Enterprise Unlimited N/A

Rate Limit Headers

All API responses include rate limit headers:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1702734600

Header Descriptions

  • X-RateLimit-Limit: Maximum requests allowed in the window
  • X-RateLimit-Remaining: Requests remaining in current window
  • X-RateLimit-Reset: Unix timestamp when limit resets

Rate Limit by Endpoint Type

Different endpoint types have different limits:

Endpoint Type Free Starter Pro Team
Authentication 10/min 20/min 75/min 150/min
Document Upload 10/min 20/min 30/min 50/min
AI Processing 5/min 15/min 20/min 50/min
Chat 10/min 25/min 50/min 100/min
Search 20/min 50/min 100/min 200/min
General API 15/min 30/min 120/min 300/min

Rate Limit Response

When rate limit is exceeded:

Status: 429 Too Many Requests

{
  "error": "rate_limit_exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded. Please try again later.",
  "details": {
    "limit": 120,
    "remaining": 0,
    "reset_at": "2025-12-16T10:31:00Z"
  }
}

Handling Rate Limits

Check Headers

Always check rate limit headers:

response = requests.get(url, headers=headers)

limit = int(response.headers.get('X-RateLimit-Limit', 0))
remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
reset = int(response.headers.get('X-RateLimit-Reset', 0))

if remaining < 10:
    # Approaching limit - slow down
    time.sleep(1)

Implement Exponential Backoff

import time
from datetime import datetime

def make_request_with_backoff(url, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        
        if response.status_code == 429:
            # Rate limited
            error = response.json()
            reset_at = error.get('details', {}).get('reset_at')
            
            if reset_at:
                # Wait until reset time
                reset_time = datetime.fromisoformat(reset_at.replace('Z', '+00:00'))
                wait_seconds = (reset_time - datetime.now()).total_seconds()
                if wait_seconds > 0:
                    time.sleep(wait_seconds)
            else:
                # Exponential backoff
                wait_time = 2 ** attempt
                time.sleep(wait_time)
            
            continue
        
        return response
    
    raise Exception("Max retries exceeded")

Rate Limit Middleware

import time
from functools import wraps

class RateLimiter:
    def __init__(self, max_calls, period):
        self.max_calls = max_calls
        self.period = period
        self.calls = []
    
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            # Remove calls outside the period
            self.calls = [call_time for call_time in self.calls if now - call_time < self.period]
            
            if len(self.calls) >= self.max_calls:
                # Wait until oldest call expires
                sleep_time = self.period - (now - self.calls[0])
                if sleep_time > 0:
                    time.sleep(sleep_time)
            
            self.calls.append(time.time())
            return func(*args, **kwargs)
        return wrapper

# Usage
@RateLimiter(max_calls=120, period=60)
def make_api_request():
    return requests.get(url, headers=headers)

Best Practices

Monitor Rate Limits

  1. Check headers - Always check X-RateLimit-Remaining
  2. Log warnings - Log when approaching limits
  3. Track usage - Monitor your API usage

Optimize Requests

  1. Batch operations - Use batch endpoints when available
  2. Cache responses - Cache responses to reduce API calls
  3. Pagination - Use pagination instead of fetching all data
  4. Webhooks - Use webhooks instead of polling

Handle Limits Gracefully

  1. Implement backoff - Use exponential backoff
  2. Queue requests - Queue requests when at limit
  3. User feedback - Inform users of rate limits
  4. Upgrade plan - Consider upgrading for higher limits

Rate Limit Examples

Python

import requests
import time
from datetime import datetime

class RateLimitedClient:
    def __init__(self, api_key, tenant):
        self.api_key = api_key
        self.tenant = tenant
        self.base_url = "https://api.archivus.app/api/v1"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "X-Tenant-Subdomain": tenant
        }
    
    def _check_rate_limit(self, response):
        remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
        if remaining < 5:
            reset = int(response.headers.get('X-RateLimit-Reset', 0))
            wait_time = reset - int(time.time())
            if wait_time > 0:
                print(f"Rate limit approaching. Waiting {wait_time} seconds...")
                time.sleep(wait_time)
    
    def get(self, endpoint):
        response = requests.get(f"{self.base_url}/{endpoint}", headers=self.headers)
        self._check_rate_limit(response)
        return response
    
    def post(self, endpoint, data):
        response = requests.post(
            f"{self.base_url}/{endpoint}",
            headers=self.headers,
            json=data
        )
        self._check_rate_limit(response)
        return response

JavaScript

class RateLimitedClient {
  constructor(apiKey, tenant) {
    this.apiKey = apiKey;
    this.tenant = tenant;
    this.baseURL = 'https://api.archivus.app/api/v1';
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'X-Tenant-Subdomain': tenant
    };
  }
  
  async checkRateLimit(response) {
    const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');
    if (remaining < 5) {
      const reset = parseInt(response.headers.get('X-RateLimit-Reset') || '0');
      const waitTime = reset - Math.floor(Date.now() / 1000);
      if (waitTime > 0) {
        console.log(`Rate limit approaching. Waiting ${waitTime} seconds...`);
        await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
      }
    }
  }
  
  async get(endpoint) {
    const response = await fetch(`${this.baseURL}/${endpoint}`, {
      headers: this.headers
    });
    await this.checkRateLimit(response);
    return response;
  }
  
  async post(endpoint, data) {
    const response = await fetch(`${this.baseURL}/${endpoint}`, {
      method: 'POST',
      headers: { ...this.headers, 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
    await this.checkRateLimit(response);
    return response;
  }
}

Upgrading Limits

Check Current Tier

GET /api/v1/subscription/status

Upgrade Plan

Contact sales@ubiship.com or use the upgrade endpoint:

POST /api/v1/subscription/checkout

Next Steps


Questions? Check the FAQ or contact support@ubiship.com