Skip to content
On this page

Rate Limiting

Scottfree Sports enforces both short-window request limits and monthly quotas.

API Plans

PlanMonthly quotaPer-second limit
Basic50,000 requests5 requests/second
Premium200,000 requests50 requests/second

Sports Data has a separate quota: 8 historical dataset refreshes per calendar month.

Headers

Successful rate-limited responses include:

text
X-RateLimit-Limit: 50000
X-RateLimit-Remaining: 49872
X-RateLimit-Reset: 1780272000
X-RateLimit-Tier: basic
HeaderMeaning
X-RateLimit-LimitEffective monthly API request limit for this key
X-RateLimit-RemainingRemaining requests in the current monthly window
X-RateLimit-ResetUnix timestamp for reset
X-RateLimit-Tierbasic or premium

Checking Usage

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/customers/me/usage

Response:

json
{
  "monthly_requests": 128,
  "monthly_limit": 50000,
  "requests_remaining": 49872,
  "tier": "basic",
  "api_plan": "basic",
  "api_subscription_status": "active",
  "subscription_status": "active",
  "reset_date": "2026-06-01T00:00:00+00:00"
}

Rate Limit Errors

When a key exceeds its allowed rate or quota, the API returns 429:

json
{
  "error": "HTTP_429",
  "detail": "Rate limit exceeded. Limit: 50000 requests per month",
  "timestamp": "2026-05-23T16:00:00.000000"
}

Use a simple retry with backoff for 429, 502, 503, and 504:

python
import os
import time
import requests

API_KEY = os.environ["SFS_API_KEY"]
BASE_URL = "https://sports-api.scottfreellc.com"

def get_json(path, params=None, attempts=3):
    for attempt in range(attempts):
        response = requests.get(
            f"{BASE_URL}{path}",
            headers={"X-API-Key": API_KEY},
            params=params,
            timeout=30,
        )
        if response.status_code == 200:
            return response.json()
        if response.status_code in {429, 502, 503, 504} and attempt < attempts - 1:
            time.sleep(2 ** attempt)
            continue
        response.raise_for_status()

data = get_json("/api/v1/predictions/mlb/won_on_points")

Practical Guidance

  • Cache responses locally for dashboards that refresh often.
  • Prefer one request per sport/model combination, then filter client-side.
  • Do not poll predictions every few seconds. Model outputs are generated by scheduled pipeline runs, not every second.
  • Use /api/v1/customers/me/usage before large batch jobs.
  • For Premium MCP sessions, remember that assistant tool calls consume the same API quota as direct REST calls.

Sports model data and research tools