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 -s "https://api.scrapercompany.com/v1/usage?period=24h" \
-H "x-api-key: $SCRAPERCOMPANY_API_KEY" | jq .keyPlanned defaults for new keys on each plan (paid plans are not on sale yet):
| Plan | Requests / minute per key | Concurrent 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.
| detail | Cause |
|---|---|
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"])Higher limits
Need more than your key allows? Contact sales with your expected volume; per-key limits can be raised during the beta.