---
title: Conventions & limits
description: Pagination, date formats, status values, error shape, rate limits and caching — the rules shared by every BSD API.
---

# 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/` |
| Odds API | `/odds/api/` |
| 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** (the Odds API caps at 50).

```bash
GET /api/v2/events/?limit=50&offset=50   # items 51–100
```

```json
{
  "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 |

## 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:

```json
{ "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](/docs/authentication/) |
| `402` | Valid token but the endpoint needs a paid add-on |
| `404` | Resource doesn't exist |
| `429` | Rate limit exceeded — back off and retry |
| `503` | Feature temporarily unavailable (e.g. a prediction model is offline) |

## Rate limits

There is **no per-account quota** — but there is a per-IP burst limit of
**10 requests/second (burst 110)** on all API paths. Exceeding it returns
`429`. If you need sustained high throughput, spread requests out or cache
responses on your side; hot list endpoints are already cheap to poll (see
below).

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.
- Images are cached aggressively (up to 30 days) — see [Image API](/docs/images/).

Poll live lists no more often than every 10 s; for true real-time data use the
[WebSockets](/docs/websocket/) instead of polling.
