Skip to content

Rate Limits

Updated Feb 2026

Firecrawl enforces rate limits to ensure fair usage and availability of the API for all users. Rate limits are measured in requests per minute and vary by plan and endpoint. When configured correctly, your real bottleneck will be concurrent browsers, not rate limits.

Billing Model

Firecrawl uses subscription-based monthly plans. There is no pure pay-as-you-go model, but the auto-recharge feature provides flexible scaling -- once you subscribe to a plan, you can automatically purchase additional credits when you dip below a threshold, with better rates on larger packs. For testing before committing, start with the Free or Hobby tier.

Plan downgrades are scheduled to take effect at the next renewal, and unused-time credits are not issued.

See Billing & Plans for full details on credits and pricing.

Concurrent Browser Limits

Concurrent browsers represent how many web pages Firecrawl can process for you at the same time. Your plan determines how many jobs can run simultaneously -- if you exceed this limit, additional jobs wait in a queue until resources become available.

Current Plans

PlanConcurrent Browsers
Free2
Hobby5
Standard50
Growth100
Scale / Enterprise150+

Extract Plans (Legacy)

PlanConcurrent Browsers
Free2
Starter50
Explorer100
Pro200

Need Higher Concurrency?

If you require higher concurrency limits, contact help@firecrawl.com to discuss custom plans.

API Rate Limits

Rate limits are measured in requests per minute and are primarily in place to prevent abuse.

Current Plans

Plan/scrape/map/crawl/search/agent/crawl/status/agent/status
Free101015101,500500
Hobby10010015501001,50025,000
Standard500500502505001,50025,000
Growth5,0005,0002502,5001,0001,50025,000
Scale7,5007,5007507,5001,00025,00025,000

Extract Endpoints

The extract endpoints (/v2/extract) share rate limits with the corresponding /agent rate limits shown above.

Batch Scrape Endpoints

The batch scrape endpoints (/v2/batch/scrape) share rate limits with the corresponding /crawl rate limits shown above.

FIRE-1 Agent

Requests involving the FIRE-1 agent have separate rate limits that are counted independently:

EndpointRate Limit (requests/min)
/scrape10
/extract10

Extract Plans (Legacy)

Plan/extract (requests/min)/extract/status (requests/min)
Starter10025,000
Explorer50025,000
Pro1,00025,000

Handling 429 Responses

When you exceed a rate limit, the API returns an HTTP 429 (Too Many Requests) response. Your application should handle this gracefully.

Response Format

json
{
  "success": false,
  "error": "Rate limit exceeded. Please retry after some time."
}

Response Headers

Firecrawl includes rate limit information in response headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per minute for this endpoint
X-RateLimit-RemainingNumber of requests remaining in the current window
X-RateLimit-ResetUnix timestamp when the rate limit window resets
Retry-AfterSeconds to wait before retrying (on 429 responses)

Retry Strategies

The best approach for handling rate limits is exponential backoff with jitter:

javascript
async function firecrawlRequest(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      const baseDelay = retryAfter
        ? parseInt(retryAfter) * 1000
        : Math.pow(2, attempt) * 1000;

      // Add jitter to prevent thundering herd
      const jitter = Math.random() * 1000;
      const delay = baseDelay + jitter;

      console.log(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1})`);
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }

    return response;
  }

  throw new Error('Max retries exceeded');
}

Python with Exponential Backoff

python
import time
import random
import requests

def firecrawl_request(url, headers, json_data, max_retries=5):
    for attempt in range(max_retries):
        response = requests.post(url, headers=headers, json=json_data)

        if response.status_code == 429:
            retry_after = response.headers.get('Retry-After')
            if retry_after:
                delay = int(retry_after)
            else:
                delay = (2 ** attempt) + random.uniform(0, 1)

            print(f"Rate limited. Retrying in {delay:.1f}s (attempt {attempt + 1})")
            time.sleep(delay)
            continue

        return response

    raise Exception("Max retries exceeded")

Best Practices

PracticeDescription
Use exponential backoffDouble the wait time between retries: 1s, 2s, 4s, 8s, 16s...
Add jitterAdd random delay to prevent multiple clients from retrying simultaneously
Respect Retry-AfterWhen present, use this header value instead of your own backoff calculation
Use webhooksFor crawl and batch scrape, use webhooks instead of polling /crawl/status to avoid hitting status endpoint limits
Batch requestsUse /v2/batch/scrape instead of making many individual /v2/scrape calls
Cache resultsUse the maxAge parameter to leverage Firecrawl's built-in caching and avoid re-scraping recently fetched pages

Rate Limit Quick Reference

For fast lookups during development:

If you are on...You can call /scrape...And /crawl...
Free10 times/min1 time/min
Hobby100 times/min15 times/min
Standard500 times/min50 times/min
Growth5,000 times/min250 times/min
Scale7,500 times/min750 times/min