Important

Rate Limits

To ensure fair usage and platform stability, all API requests are subject to rate limiting. Understand your limits and how to handle them gracefully.

Limits by Plan

Plan
Requests/min
Requests/day
Burst
Price
Free
30
1,000
10
$0
Pro POPULAR
150
25,000
50
$49/mo
Business
600
100,000
200
$199/mo
Enterprise
Custom
Unlimited
Custom

How Rate Limiting Works

Sliding Window

We use a sliding window counter. Your limit resets gradually — not all at once. Each second, used capacity from 60 seconds ago is freed up.

Burst Allowance

A burst allowance lets you send a short spike of requests (e.g., 10 at once on Free) before the per-minute limit kicks in. Useful for batch loading.

Daily Reset

Daily limits reset at midnight UTC. Once exceeded, all requests return 429 until the next reset period begins.

Rate Limit Headers

Every API response includes headers that tell you your current rate limit status. Use these to build adaptive request logic.

Header
Example
Description
X-RateLimit-Limit
150
Max requests per minute for your plan
X-RateLimit-Remaining
87
Requests left in the current window
X-RateLimit-Reset
1721824530
Unix timestamp when the window resets
X-RateLimit-DailyLimit
25000
Total daily request quota for your plan
X-RateLimit-DailyRemaining
18340
Requests remaining today (resets at UTC midnight)
Retry-After
12
Seconds to wait before retrying (only on 429)

Response Header Example

Here's what the response headers look like on a real request:

HTTP Response Headers
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 150
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1721824530
X-RateLimit-DailyLimit: 25000
X-RateLimit-DailyRemaining: 18340
X-Request-Id: req_8f14e45fceea167a5a36dedd4bea2543

When You Hit the Limit

When you exceed your rate limit, the API returns a 429 Too Many Requests status with a structured error body:

429 Response Body
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 12 seconds.",
    "status": 429,
    "details": {
      "limit": 150,
      "remaining": 0,
      "reset_at": "2025-07-24T10:15:30Z",
      "retry_after": 12,
      "daily_limit": 25000,
      "daily_remaining": 18340
    }
  }
}
Copied to clipboard

How to Handle Rate Limits

Follow these strategies to build resilient integrations that never break under load.

backoff.js
async function fetchWithBackoff(url, options = {}, maxRetries = 5) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) return response;

    // Respect the Retry-After header
    const retryAfter = response.headers.get('Retry-After');
    const waitTime = retryAfter
      ? parseInt(retryAfter) * 1000
      : Math.min(1000 * Math.pow(2, attempt), 30000);

    console.warn(`Rate limited. Retrying in ${waitTime}ms...`);
    await new Promise(r => setTimeout(r, waitTime));
  }
  throw new Error('Max retries exceeded');
}

Best Practices

Respect the Retry-After header

Always read the Retry-After header from 429 responses. Don't guess — the server tells you exactly how long to wait.

Use exponential backoff with jitter

When the Retry-After header is absent, use exponential backoff (1s, 2s, 4s, 8s…) with random jitter to avoid thundering herd scenarios.

Cache responses when possible

Sports and league lists rarely change. Cache them locally for 5–15 minutes to dramatically reduce API calls and stay well under your limits.

Monitor your usage in real-time

Log X-RateLimit-Remaining on every request. Set up alerts at 80% and 95% consumption to catch issues before they hit production.

Don't poll endpoints unnecessarily

Avoid polling unchanged data. Use WebSocket connections (coming soon) for live odds instead of repeatedly hitting GET endpoints.

Don't share API keys across services

Each service should have its own key so rate limits are isolated. One misbehaving service shouldn't bring down another.

Usage Gauge

Here's a visual representation of how your per-minute quota depletes and recovers over time:

Per-minute usage 87 / 150 remaining
0 requests 150 requests
Daily usage 18,340 / 25,000 remaining
0 requests 25,000 requests
Burst allowance 42 / 50 remaining
0 used 50 burst

Endpoint-Specific Notes

Some endpoints have additional constraints beyond your plan limits:

Live endpoints are more restrictive

Endpoints returning live/in-play data (/live-events, /markets/v2) consume 2x the normal rate limit quota per request.

Bulk endpoints have lower frequency limits

Endpoints that return large datasets (/leagues/{id}/matches, /leagues/{id}/results) are limited to 1 request per 5 seconds regardless of your plan.

List endpoints are lightweight

Simple list endpoints (/sports, /sports/{id}/leagues) consume 0.5x the normal rate — they're cheap and fast.

Endpoint Type
Cost Multiplier
Min Interval
List / Enumerate
×0.5
None
Standard GET
×1.0
None
Live / In-Play
×2.0
None
Bulk / Paginated
×1.0
5 seconds

Frequently Asked Questions

All subsequent requests return 429 until midnight UTC. Your per-minute limit continues to function independently within the daily cap. Consider upgrading your plan if you're consistently hitting daily limits.
Rate limits are enforced per API key. If you have multiple keys on the same account, each key has its own independent limit. This is why we recommend separate keys per service.
Yes. Enterprise plans offer fully customizable rate limits. Contact our sales team with your expected request volume and use case, and we'll configure limits that match your needs.
No. WebSocket connections (available on Pro plans and above) have separate connection limits. Only REST API requests count toward your rate limit quota. Incoming WebSocket messages are unlimited.
No. Rejected requests (429 responses) do not count against your quota. Only successful (2xx) and client-error (4xx, excluding 429) responses consume your rate limit.

Need more headroom?

Upgrade to Pro or Business for 5–20x more requests, or talk to us about a custom Enterprise plan.

Upgrade Plan