Rate Limits
Orsa enforces rate limits to ensure fair usage and platform stability. Limits vary by plan.
Limits by Plan
| Plan | Requests / minute | Requests / day | Concurrent |
|---|---|---|---|
| Free | 10 | 100 | 2 |
| Starter | 60 | 5,000 | 5 |
| Pro | 300 | 50,000 | 20 |
| Enterprise | Custom | Custom | Custom |
Rate Limit Headers
Every API response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait (only on 429 responses) |
Example Response Headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1713024000
X-Request-Id: a1b2c3d4-e5f6-7890-abcd-ef1234567890Handling Rate Limits
When you exceed the limit, the API returns a 429 Too Many Requests response:
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again in 30 seconds."
}
}Retry Strategy (TypeScript)
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
baseDelay = 1000,
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
if (error.status !== 429 || attempt === maxRetries) throw error;
const retryAfter = error.headers?.['retry-after'];
const delay = retryAfter
? parseInt(retryAfter) * 1000
: baseDelay * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
// Usage
const brand = await withRetry(() =>
client.brand.retrieve({ domain: 'stripe.com' })
);Retry Strategy (Python)
import time
def with_retry(fn, max_retries=3, base_delay=1.0):
for attempt in range(max_retries + 1):
try:
return fn()
except Exception as e:
if getattr(e, 'status', None) != 429 or attempt == max_retries:
raise
retry_after = getattr(e, 'retry_after', None)
delay = float(retry_after) if retry_after else base_delay * (2 ** attempt)
time.sleep(delay)
raise Exception("Max retries exceeded")
# Usage
brand = with_retry(lambda: client.brand.retrieve(domain="stripe.com"))Best Practices
- Respect
Retry-Afterheaders. They tell you exactly how long to wait. - Use exponential backoff as a fallback when
Retry-Afterisn't present. - Prefetch to spread load. Use the free prefetch endpoints to warm cache before batch operations.
- Deduplicate requests. Multiple lookups for the same domain in quick succession waste your rate limit budget.
- Monitor
X-RateLimit-Remaining. Throttle proactively when you're running low instead of waiting for 429s. - Use webhooks for crawl jobs instead of polling — reduces unnecessary API calls.
Burst Handling
Short bursts above the per-minute limit are tolerated within reason. The rate limiter uses a sliding window — you won't be cut off the instant you hit the limit if the previous windows were light.
Need Higher Limits?
Contact hello@orsa.dev for Enterprise plans with custom rate limits, dedicated infrastructure, and SLA guarantees.