Guides
Get Started
Rate Limits

Rate Limits

Orsa enforces rate limits to ensure fair usage and platform stability. Limits vary by plan.

Limits by Plan

PlanRequests / minuteRequests / dayConcurrent
Free101002
Starter605,0005
Pro30050,00020
EnterpriseCustomCustomCustom

Rate Limit Headers

Every API response includes rate limit headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds 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-ef1234567890

Handling 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-After headers. They tell you exactly how long to wait.
  • Use exponential backoff as a fallback when Retry-After isn'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.