Documentation
Platform API Overview

Rate Limits

Understand API rate limits, monitor usage headers, and handle 429 responses gracefully.

Rate Limits

ALT Sports Data enforces rate limits to protect system stability and ensure fair access for all consumers. Every response includes headers that tell you exactly where you stand.

Diagram showing how rate limits, usage windows, and retries work across ALT Sports Data requests.

Rate limit headers

Every API response includes these headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1678886400
HeaderMeaning
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests you have left before hitting the limit
X-RateLimit-ResetUnix timestamp (seconds) when the window resets

Rate limit tiers

TierRequests / HourRequests / Day
Demo1001,000
Free1,00010,000
Pro10,000100,000
EnterpriseCustomCustom

What happens when you hit the limit

When you exceed your quota, the API returns a 429 Too Many Requests response:

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please retry after 1678886400",
    "retry_after": 1678886400
  }
}

The retry_after field is a Unix timestamp. Wait until that time before sending the next request.

Retry with exponential backoff

The most reliable way to handle rate limits is to read the X-RateLimit-Reset header and sleep until the window resets. If the header is missing, fall back to exponential backoff.

import requests
import time

def request_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code != 429:
            return response

        # Use the reset timestamp if available, otherwise back off exponentially
        reset_ts = response.headers.get("X-RateLimit-Reset")
        if reset_ts:
            wait = max(int(reset_ts) - int(time.time()), 1)
        else:
            wait = 2 ** attempt

        print(f"Rate limited (attempt {attempt + 1}). Waiting {wait}s...")
        time.sleep(wait)

    raise Exception(f"Still rate-limited after {max_retries} retries")
async function requestWithRetry(url, headers, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });

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

    // Use the reset timestamp if available, otherwise back off exponentially
    const resetTs = response.headers.get("X-RateLimit-Reset");
    const wait = resetTs
      ? Math.max(parseInt(resetTs) - Math.floor(Date.now() / 1000), 1)
      : 2 ** attempt;

    console.log(`Rate limited (attempt ${attempt + 1}). Waiting ${wait}s...`);
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }

  throw new Error(`Still rate-limited after ${maxRetries} retries`);
}

Best practices

  1. Monitor the headers -- check X-RateLimit-Remaining after each response so you know how close you are to the ceiling
  2. Cache when possible -- sports and league metadata changes infrequently; cache it for minutes or hours rather than re-fetching on every page load
  3. Use filters and pagination -- narrow your queries with parameters like sportType, eventStatus, and limit to reduce the number of calls
  4. Handle 429s gracefully -- never retry in a tight loop; always wait for the reset time or use exponential backoff
  5. Batch where the API allows -- a single filtered request is cheaper than many unfiltered ones

Need higher limits?

If your use case requires more throughput, contact connect@altsportsdata.com to discuss Pro or Enterprise plans with custom quotas.

On this page