Skip to content
Product Documentation

Rate limits & quotas

Two independent limits apply to every key:

  • Request rate — requests per minute, per key. Exceeding it returns a 429 with problem type rate-limited.
  • Monthly row quota — rows downloaded per calendar month, per account. Exhausting it returns a 429 with problem type quota-exhausted, plus reset (when it clears) and upgrade_url extensions.

A third, rare 429 (problem type throttled) is an operations kill-switch on a specific key — see the three 429s.

Tier Requests per minute
DEMO 30
BETA 120
STARTER 300
PRO 1000

Monthly row quotas for the paid tiers are still being finalized; while your tier has no cap configured, GET /v1/me reports monthly_rows: null and no quota gate applies.

Every dataset request debits the artifact’s full row count — the number of rows in the Parquet file, not the number you end up using:

  • Filters don’t reduce spend. The server never filters; the SDK/CLI filter locally after download. --operator FANDUEL shapes what you see, not what you’re charged.
  • Range reads debit in full. A partial (HTTP Range) read — including DuckDB’s predicate-pushdown reads — is still a read of that artifact.
  • Discovery is cheap. JSON endpoints debit the rows they return (a page of games is a page of rows).

The lever you control is which files you fetch: narrow date ranges and per-game fetches are the cheap path.

Typical rows per game: closing-lines ≈ 2,400 · market-results ≈ 12,000 · sportsbook-lines ≈ 190,000 · game-state-timeline is small.

GET /v1/me reports the calling key’s tier, rate limit, and quota usage. It stays reachable even when your monthly quota is exhausted, and reading it never consumes rows:

Terminal window
curl -s "https://api.betflux.ai/v1/me" \
-H "Authorization: Bearer $BETFLUX_API_KEY"
{
"key_id": "…",
"tier": "PRO",
"rate_limit_per_minute": 1000,
"quota": {
"monthly_rows": null,
"used_rows": 412300,
"resets": "2026-08-01"
}
}

monthly_rows: null means no row cap is configured for the tier; used_rows is a best-effort counter of rows downloaded this month; resets is the UTC date the monthly quota rolls over. betflux keys check prints the same information.

Successful responses carry no rate-limit headers — use /v1/me to see your limits and usage. When a request is rejected with a rate-limit 429, the response carries the IETF RateLimit headers:

Header Meaning
RateLimit-Limit Requests allowed per minute for your tier
RateLimit-Remaining 0 — the window is spent
RateLimit-Reset Seconds until the window resets
Retry-After Seconds to wait before retrying

A quota-exhausted 429 also carries Retry-After, but it counts down to the monthly reset — don’t sleep on it. Stop until the reset date in the body, or upgrade via its upgrade_url.

  • Fetch each game’s file once and query it locally as many times as you like — re-downloading is what costs quota.
  • Use /v1/games filters (league, date range, team, status) to shrink the set of files you fetch; the SDK forwards its team filter to discovery for exactly this reason.
  • The Python SDK retries rate-limit 429s and 5xx automatically with backoff that honors Retry-After (capped at 120 s); quota 429s raise QuotaExceededError immediately.
  • --limit / max_rows on SDK range queries stops fetching further games once satisfied — it caps spend, not just output.