Skip to content

Rate limits

The per-key requests-per-minute limit, concurrency limits and how to back off.

Per-key limit

Each API key may send a fixed number of requests per minute, counted over a sliding 60-second window. Beta keys default to 60 requests per minute. Your key's current limit is key.rate_limit_per_min in GET/v1/usage:

cURL
curl -s "https://api.scrapercompany.com/v1/usage?period=24h" \
  -H "x-api-key: $SCRAPERCOMPANY_API_KEY" | jq .key

Planned defaults for new keys on each plan (paid plans are not on sale yet):

PlanRequests / minute per keyConcurrent requests
Free
60
2
Starter
300
10
Growth
1,200
40
Scale
3,000
100

Concurrency

Plans also cap how many metered requests can be in flight at once (2 on Free up to 100 on Scale). The cap is not enforced during the beta; it applies once credit enforcement is switched on. Free operations such as usage and billing reads are never counted.

When you hit a limit

The API returns 429 with a detail string. There are no X-RateLimit-* or Retry-After headers, so pace requests on your side and back off when you see a 429. Rate-limited requests are not charged.

detailCause
rate limit 60/min exceeded
More than your per-minute limit in the last 60 seconds.
concurrency limit 2 reached for the Free plan
Too many requests in flight (enforcement on only).

Backoff example

Pace calls to stay under the limit, and retry 429 (and 502/503) with exponential backoff plus jitter:

import os
import random
import time

import requests

LIMIT_PER_MIN = 60                     # key.rate_limit_per_min from GET /v1/usage
MIN_INTERVAL = 60 / LIMIT_PER_MIN
session = requests.Session()
session.headers["x-api-key"] = os.environ["SCRAPERCOMPANY_API_KEY"]
_last = 0.0

def post(path, body):
    global _last
    for attempt in range(6):
        wait = MIN_INTERVAL - (time.monotonic() - _last)
        if wait > 0:
            time.sleep(wait)
        _last = time.monotonic()
        r = session.post(f"https://api.scrapercompany.com{path}", json=body, timeout=120)
        if r.status_code not in (429, 502, 503):
            r.raise_for_status()
            return r.json()
        time.sleep(min(2 ** attempt, 60) + random.random())
    r.raise_for_status()

for token in ["ChUIoben2Mv6-CYaCi9tLzA3czVwbjQQAQ", "ChcI7LnnsZLasKYOGgsvZy8xdHJzejFiORAB"]:
    print(post("/v1/calendar", {"token": token, "days": 90})["coverage"])

Batch instead of looping

For many properties, submit one calendar job with up to 50 items instead of 50 separate calls.

Higher limits

Need more than your key allows? Contact sales with your expected volume; per-key limits can be raised during the beta.