Skip to main content

API Rate Limits

Our API employs rate limiting to ensure fair usage and protect against abuse. Exceeding the defined limits will result in your requests being temporarily blocked.

Limit Details​

Rate limiting is applied based on the requester's IP address. The current limit is 6000 requests per 5-minute period. This averages to 20 requests per second.

If you exceed this limit, you will receive an HTTP 429 Too Many Requests response code.

If your application requires a higher request rate, please contact us to discuss your requirements.

Per-operation limits​

A few operations carry their own limit on top of the overall one, because each call is expensive or has effects beyond returning data. These are counted per authenticated user or service account — per IP address for anonymous requests — and separately for each operation, so exhausting one leaves the rest unaffected.

OperationLimit
createSelfServeAccount3 per minute
createCheckoutSession3 per minute

Exceeding one of these does not produce an HTTP 429. The request succeeds at the HTTP level and returns a GraphQL error with code RATE_LIMIT_EXCEEDED, carrying the seconds to wait in extensions.retryAfter:

{
"errors": [
{
"message": "Too many requests. Please try again in 41 seconds.",
"extensions": { "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 41 }
}
]
}

Prefer retryAfter over a backoff schedule of your own here: it is the exact time remaining in the window.

Exponential backoff​

When you receive a 429 response, wait before retrying. Use exponential backoff to progressively increase the delay between retries:

async function fetchWithBackoff(url: string, options: RequestInit, maxRetries = 5): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);

if (response.status !== 429) {
return response;
}

const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
const jitter = Math.random() * 500;
console.warn(`Rate limited. Retrying in ${Math.round(delay + jitter)}ms...`);
await new Promise(resolve => setTimeout(resolve, delay + jitter));
}

throw new Error("Max retries exceeded due to rate limiting");
}

Adding random jitter prevents multiple clients from retrying in lockstep.

Request budgeting​

To stay within limits proactively:

  • Batch related data into fewer, larger queries. GraphQL lets you request multiple fields and connections in a single request.
  • Cache stable data such as report templates, account metadata, and finished crawl results.
  • Use report downloads for bulk data export instead of paginating through thousands of API pages.
  • Track your usage by counting requests in your client code and throttling when approaching the limit.

A simple in-memory rate tracker:

class RateTracker {
private timestamps: number[] = [];
private readonly limit = 6000;
private readonly windowMs = 5 * 60 * 1000;

canMakeRequest(): boolean {
const now = Date.now();
this.timestamps = this.timestamps.filter(t => now - t < this.windowMs);
return this.timestamps.length < this.limit;
}

recordRequest(): void {
this.timestamps.push(Date.now());
}
}