Getting Started
Two quotas apply per key: per-minute and per-day. Both are checked on every request and reported in response headers so clients can back off gracefully.
| Tier | Per minute | Per day | History window |
|---|---|---|---|
| Sandbox | 3 | 100 | 60 days |
| Student | 20 | 1,000 | 180 days |
| Institutional | 30 | 5,000 | 365 days |
| Starter | 60 | 10,000 | 365 days |
| Growth | 300 | 100,000 | Unlimited |
| Enterprise | 600+ | Unlimited | Unlimited |
Both limits are enforced on a rolling window. Exceeding either returns 429 quota_exceeded with a Retry-After header (seconds until the next slot frees up).
On top of the call quotas, the free tiers (Sandbox and Student) carry two data-volume limits that keep the API open for evaluation and learning without enabling bulk archive extraction. Paid tiers (Starter and above) are not subject to either.
| Limit | Sandbox | Student | Over-limit response |
|---|---|---|---|
| History depth | 60 days | 180 days | 422 window_out_of_plan |
| Rows per call | 50,000 | 100,000 | 422 per_call_row_cap_exceeded |
| Rows per 30 days | 500,000 | 2,000,000 | 429 export_cap_exceeded |
422 window_out_of_plan with allowed.earliest — the window is not silently truncated.422 per_call_row_cap_exceeded. Narrow the window or add filters.429 export_cap_exceeded with used, cap, and resets_at; capacity frees as older usage ages out of the window.Hitting a free-tier limit?
Each of these responses carries an upgrade link to /developer/pricing. Starter and higher lift the depth window to 365 days or more and remove the per-call and 30-day row caps.
Every 2xx and 429 response includes:
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 58
X-RateLimit-Limit-Day: 10000
X-RateLimit-Remaining-Day: 9842
X-Tier: starter
Retry-After: 12 # only on 429Limit fields are constant for a given tier — useful as a sanity check but you can hard-code them.Remaining fields are the authoritative source of truth. Always read them instead of counting requests client-side.X-Tier tells you which plan billed the request. Helpful in logs when you upgrade and want to confirm the key picked up the new tier.Back off based on Retry-After. Exponential backoff with jitter is the safest default:
import time, random, requests
def get(url, key, attempt=0):
r = requests.get(url, headers={"X-API-Key": key}, timeout=10)
if r.status_code == 429 and attempt < 5:
delay = int(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(delay + random.random())
return get(url, key, attempt + 1)
r.raise_for_status()
return r.json()Pre-emptive throttling is cheaper than retrying
Once X-RateLimit-Remaining-Minute drops below ~10% of the limit, pause your worker loop until the next minute boundary instead of pounding through the remaining budget.
The per-minute limit is a hard ceiling — bursts above it are rejected immediately, not queued. The per-day limit is informational until exhausted, then also hard-rejects.
If you need sustained throughput above your tier's per-minute limit, upgrade to the next tier or contact sales for a bespoke limit.