Growing Discord community — direct access to the developer, live coverage & picks. Join the Discord Join now →
Matches Leagues Predictions The Edge Money News Stats Coverage
Docs / Getting Started

Conventions & limits#

Every BSD API follows the same conventions. Learn them once and every endpoint on this site behaves the same way.

Base URL#

All APIs live under one host:

https://sports.bzzoiro.com
Family Base path
Football v2 (recommended) /api/v2/
Football v1 (legacy) /api/
Tennis / CS2 / Darts / Hockey / Basketball / Horse racing /<sport>/api/v2/
Signals /api/signals/
MCP servers /mcp and /<sport>/mcp
WebSockets wss://sports.bzzoiro.com/live/football/ and /ws/live/
Images /img/

Pagination#

List endpoints are paginated with limit and offset. Default page size is 50, maximum 200.

GET /api/v2/events/?limit=50&offset=50   # items 51–100
{
  "count": 408,
  "next": "https://sports.bzzoiro.com/api/v2/events/?limit=50&offset=100",
  "previous": "https://sports.bzzoiro.com/api/v2/events/?limit=50",
  "results": [ ... ]
}

Dates and times#

  • Timestamps are ISO 8601 in UTC: 2026-08-02T14:00:00+00:00.
  • Date filters take YYYY-MM-DD: ?date_from=2026-08-01&date_to=2026-08-07.
  • The football v1 API accepts a tz parameter to shift datetimes; v2 always returns UTC — convert client-side.

Status values#

Status Meaning
upcoming Not started yet
live In progress
finished Ended, result final
cancelled Called off before starting
postponed Rescheduled to a future date
unresolved Kickoff passed, the match was never seen played, and no result was ever recorded. Scores stay null — nothing is inferred

Odds format#

All odds everywhere are decimal (e.g. 1.85). null means the market is not priced yet. Implied probabilities, where provided, are 0–1 floats.

IDs#

Use BSD numeric ids (id fields) for everything — they are stable and work across REST, MCP, WebSockets and the Image API. Never guess ids: resolve them through list/search endpoints first.

Error shape#

Every API error is JSON with the same envelope:

{ "error": true, "status": 404, "detail": "Not found." }
Code Meaning
200 Success
400 Invalid parameter (detail says which)
401 Missing or invalid API token — see Authentication
402 Valid token but the endpoint needs a paid add-on
404 Resource doesn't exist
429 Rate limit exceeded — code says which of the two limits, see Rate limits
503 Feature temporarily unavailable (e.g. a prediction model is offline)

Unknown query parameters#

A /api/v2/ endpoint rejects any query parameter it does not accept, rather than ignoring it. This is deliberate: a dropped filter would return 200 with the unfiltered list, and there is no way to tell that apart from a filter that matched everything.

GET /api/v2/events/?league=17
{
  "detail": "Unknown query parameter(s): league. Did you mean: league -> league_id?",
  "unknown_parameters": ["league"],
  "accepted_parameters": ["date_from", "date_to", "league_id", "limit",
                          "offset", "season_id", "status", "team_id", "team_name"],
  "suggestions": { "league": "league_id" }
}

accepted_parameters is the full list that endpoint takes, so the response tells you the fix without a trip to the docs. suggestions is present only when we can guess what you meant.

Two parameters are never treated as filters and are always allowed: format (renderer switch) and token (query-string auth).

Endpoints outside /api/v2/ are unaffected.

v1 parameter names still work#

If you moved from /api/ to /api/v2/ and kept your querystring, you do not have to rewrite it. The v1 names are translated before the request is handled, so these pairs are equivalent:

v1 v2
?league=17 ?league_id=17
?season=61627 ?season_id=61627
?team=Arsenal ?team_name=Arsenal
?bookmaker=pinnacle ?bookmaker_slug=pinnacle
?page_size=50 ?limit=50
?page=3&limit=50 ?offset=100&limit=50
?date=2026-08-17 ?date_from=2026-08-17T00:00:00Z&date_to=2026-08-17T23:59:59Z

A v1 name is only translated on endpoints that have the v2 equivalent — sending ?bookmaker= to an endpoint with no bookmaker filter is still a 400. If you send both spellings, the v2 one wins.

Three more v1 parameters are accepted and do nothing, rather than erroring:

  • tz — v2 timestamps are always UTC, by contract.
  • full — the v2 list endpoints carry no spatial fields to opt into.
  • ordering — supported on /api/v2/transfers/; elsewhere the order is fixed.

Everything else is still rejected. ?leage=17 is a 400, because nobody meant to send it.

Rate limits#

Two separate limits apply, and they are checked independently.

Per-account daily quota. From 17 August 2026 the free football API allows 7,500 requests per day, counted per account and reset at midnight UTC. Going over returns 429 with "code": "taster_exhausted" and a Retry-After header holding the seconds until reset. Every response tells you where you stand — see Rate limit headers below.

Football Unlimited removes the daily quota entirely and is also what unlocks per-bookmaker prices; without it the odds endpoints return the consensus price and how many bookmakers formed it. The other sports keep their own smaller daily allowance.

Per-IP burst limit. Independently of the above, the cached read endpoints — /api/v2/events/ and /live/, /leagues/, /teams/, /tournaments/, /referees/{id}/, /venues/{id}/, /predictions/, and the legacy /api/events/ and /api/live/ — are capped at 25 requests/second (burst 110) per client address. Going over returns 429 with "code": "rate_limited" and Retry-After: 1. A paid plan does not lift this one — it exists to stop a single address from crowding everyone else out. If you need sustained throughput, spread requests out or cache responses on your side; those endpoints are already cheap to poll (see below).

The two 429s are told apart by code, and they want opposite reactions:

code Means What to do
rate_limited Per-second burst limit for your address Retry after Retry-After (1 second)
taster_exhausted Your account's daily quota is gone Retry after Retry-After (seconds to midnight UTC) — sooner is pointless

WebSocket connections are not rate-limited but each socket may hold at most 10 concurrent subscriptions.

Caching#

  • Hot football endpoints (/api/v2/events/, /leagues/, /teams/, /tournaments/) are edge-cached for ~5 seconds. The X-Cache-Status response header tells you whether you got HIT, MISS or STALE.
  • Live-score endpoints are server-cached 10–30 s; reference data (players, rankings, tournaments) about 5 minutes.
  • Odds do not behave like the 5-second endpoints: they are re-read on a schedule that runs from tens of minutes near kickoff to hours pre-match, so polling them on a fast clock returns the same rows. Use updated_after to fetch only what we have re-read since your last call — see Refresh cadence.
  • Images are cached aggressively (up to 30 days) — see Image API.

Poll live lists no more often than every 10 s; for true real-time data use the WebSockets instead of polling.

Rate limit headers#

Responses carry the two RateLimit fields from draft-ietf-httpapi-ratelimit-headers, the IETF replacement for the X-RateLimit-Limit / -Remaining / -Reset trio. We do not send the legacy X- headers.

RateLimit-Policy: "football";q=7500;w=86400
RateLimit: "football";r=7213;t=52800
  • RateLimit-Policy — the fixed shape of the policy. q is the quota, w the window in seconds. The quoted name is the scope: football, or the sport (tennis, csgo, darts, hockey, basketball, horseracing).
  • RateLimit — where you stand right now. r is what remains, t the seconds until it resets.

Windows are daily and reset at midnight UTC, not local time. Both fields are HTTP Structured Fields — parse them with a structured-fields library rather than a regex.

Accounts on a paid plan have no quota, so they receive neither field. Their absence means unlimited, not zero — do not treat a missing RateLimit as a reason to back off.

Exceed the daily allowance and you get 429 with Retry-After in seconds, alongside the same two fields with r=0.

Fair use#

the free api is provided under a fair use policy. usage that materially exceeds typical individual or small-project consumption, or that degrades service for other users, may be subject to throttling, rate limiting, or account-level restrictions. we may contact accounts with exceptional usage to discuss appropriate plans.

View this page as Markdown · Found a mistake? Tell us