Rate limits
Two independent buckets cap traffic: a per-minute sliding window and a per-day fixed UTC window. Either exhausting returns 429.
A 429 has two possible causes: quota exhaustion (the buckets above) and the per-key 4xx circuit breaker (abuse protection, described below). Tell them apart by the headers: a circuit-breaker 429 carries X-RateLimit-4xx-Cooldown, a quota 429 does not. Either way, honour Retry-After.
Tier limits
| Plan | Per minute | Per day (UTC) |
|---|---|---|
| Free | 100 | 250 (500 with GitHub) |
| Solo | 300 | 10,000 |
| Trio | 500 | 25,000 |
| Edge | 1,500 | 100,000 |
| Pro | 3,000 | 200,000 |
| Enterprise | Unlimited | Unlimited |
Webhook deliveries do not count against the request quota. WebSocket message receives do not count; explicit room subscribe/unsubscribe operations count as one request each.
GET /v1/sports and GET /v1/coverage are public and work without a key. Called without one, they share a separate, smaller per-IP limit (30/minute by default) instead of a key's plan tier — worth knowing if you are scripting against them before you have a key in hand. Every other endpoint always requires a key and uses the tier limits above.
Response headers
Every 2xx and 4xx response includes the current quota state:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1763510400
X-RateLimit-Limit-Minute: 100
X-RateLimit-Limit-Day: 250X-RateLimit-Limit and X-RateLimit-Remaining track the tighter of the two buckets; X-RateLimit-Limit-Minute and X-RateLimit-Limit-Dayexpose each bucket's cap. X-RateLimit-Reset is a Unix epoch (seconds) marking when the limiting window resets.
429: quota exhaustion
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1763510400
Content-Type: application/json{
"error": {
"code": "rate_limited",
"message": "Daily quota exhausted (250 of 250 requests used)"
},
"suggested_fix": "You've hit your free tier daily cap. Upgrade to solo at https://bigballsdata.com/pricing to lift this limit to 10,000/day.",
"docs_url": "https://bigballsdata.com/docs/rate-limits",
"support": {
"discord": "https://discord.gg/H2WJBQurbq",
"email": "[email protected]"
},
"meta": {
"request_id": "...",
"timestamp": "2026-06-23T00:00:00Z",
"current_usage": {
"minute": { "used": 12, "limit": 100, "resets_at": "2026-06-23T05:01:00Z" },
"day": { "used": 250, "limit": 250, "resets_at": "2026-06-24T00:00:00Z" }
},
"upgrade_path": {
"current_tier": "free",
"recommended_tier": "solo",
"url": "https://bigballsdata.com/pricing"
}
}
}Both 429 types share error.code: rate_limited; the error.message names the exhausted bucket and its usage. On a quota 429 the body also carries suggested_fix (the next tier and the cap it lifts you to), meta.current_usage (live per-bucket usage with reset times), and meta.upgrade_path (recommended_tier is null once you're on the top tier, where the fix points you at sales instead). All of this is additive; error.code, docs_url, support, and meta.request_id are unchanged. The per-day window does not recover until the next UTC midnight; honour Retry-After for the exact wait.
429: per-key 4xx circuit breaker
Separate from the quota buckets, each key has a circuit breaker that watches its client-error rate. Two patterns push it: a client hammering the API with malformed requests (a bad key format, an invalid path param, a wrong enum value in a tight retry loop), or a client that ignores Retry-After and keeps firing after it has exhausted its quota. Both are broken clients, and both burn capacity. The breaker protects the platform from them. It is abuse protection, not a punitive throttle: it never triggers on healthy traffic, and it clears itself automatically once you stop and wait out the cooldown.
Over a rolling 1-hour window, the breaker trips when a key crosses either threshold:
- 500 client-error (
4xx) responses in the window (the absolute gate), or - a 4xx rate of 80% or higher once at least 200 requests have been seen in the window (the ratio gate)
Quota 429s do count toward the 4xx tally, so a client stuck in a retry loop past its cap will trip the breaker rather than spin forever. A well-behaved client never does: if you honour Retry-After you back off and stay under the request floor. Successful (2xx) responses count toward the volume, not the error rate, so healthy traffic never moves you toward a trip; the breaker's own cooldown 429s are the only responses excluded entirely (so the cooldown cannot perpetuate itself).
Paid keys get a wider berth, not a different rule. A key on any plan other than Free gets 10× the absolute gate and the ratio gate's minimum sample — 5,000 4xx responses and a 2,000-request floor instead of 500 and 200. The 80% ratio gate itself is unchanged for every plan, including Free: a paid key running 80%+ unexplained 4xx over a large enough sample still trips. The wider berth exists because a paid key belongs to someone we can reach; it is not an exemption from the breaker.
On a trip, every request from that key returns 429 for a 10-minute cooldown, regardless of how much quota the key has left. A circuit-breaker 429 carries these headers (this is how you distinguish it from a quota 429):
HTTP/1.1 429 Too Many Requests
Retry-After: 600
X-RateLimit-4xx-Cooldown: 600
X-RateLimit-4xx-Reset: 1763510520
Content-Type: application/json{
"error": {
"code": "rate_limited",
"message": "Too many failed (4xx) requests. Fix your request and retry after the cooldown."
},
"docs_url": "https://bigballsdata.com/docs/rate-limits",
"support": {
"discord": "https://discord.gg/H2WJBQurbq",
"email": "[email protected]"
},
"meta": { "request_id": "...", "timestamp": "2026-05-15T20:18:00Z" }
}Retry-After and X-RateLimit-4xx-Cooldown are the same value (seconds remaining on the cooldown). X-RateLimit-4xx-Reset is the Unix epoch (seconds) when the cooldown lifts. The breaker is keyed on error rate, not request volume, so it can trip a key on any plan, including Enterprise and other unlimited keys — but not identically: the 80% ratio gate is the same for everyone, while the absolute gate and the minimum sample are 10× higher on paid plans (see above).
To avoid it: read the error.message and suggested_fix on your 4xx responses and fix the request rather than blindly retrying. A client that handles its own errors will never see this breaker.
Recovery
Honour Retry-After exactly. Do not retry sooner, the gateway returns 429 for the full window even if your token bucket would technically have refilled. Clients should:
- Read
X-RateLimit-Remainingafter every request - Self-throttle below the limit rather than rely on 429 recovery
- When the
error.messagereports a(.../day)exhaustion, queue work until the next UTC midnight (or honourRetry-After) - Never retry a non-idempotent
POSTautomatically, 429 on a webhook subscription create can mean the resource was created right before the throttle landed - On a circuit-breaker 429 (the
X-RateLimit-4xx-Cooldownheader is present), do not just wait and retry the same call, fix the failing requests first or you will re-trip the breaker as soon as the cooldown lifts
Burst handling
The per-minute bucket is a sliding window. Bursting up to the cap is fine; sustained traffic above the cap returns 429 on the first request past the limit. There is no hidden burst credit.