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
- Check headers - Always check
X-RateLimit-Remaining - Log warnings - Log when approaching limits
- Track usage - Monitor your API usage
Optimize Requests
- Batch operations - Use batch endpoints when available
- Cache responses - Cache responses to reduce API calls
- Pagination - Use pagination instead of fetching all data
- Webhooks - Use webhooks instead of polling
Handle Limits Gracefully
- Implement backoff - Use exponential backoff
- Queue requests - Queue requests when at limit
- User feedback - Inform users of rate limits
- 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
- Error Handling - Handle rate limit errors
- Authentication - Learn about authentication
- Best Practices - API best practices
Questions? Check the FAQ or contact support@ubiship.com