> ## Documentation Index
> Fetch the complete documentation index at: https://oddsstream.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> Base URL, response envelope, error codes, rate limits, and common parameters.

## Base URL

```
https://api.oddsstream.io
```

All endpoints are under this base URL. No version prefix — the API is currently at v1.

## Authentication

Every request requires an API key in the `X-Api-Key` header:

```bash theme={null}
curl "https://api.oddsstream.io/api/odds" \
  -H "X-Api-Key: os_live_YOUR_KEY"
```

Keys are issued from your dashboard at [oddsstream.io/dashboard/keys](https://oddsstream.io/dashboard/keys).
Key format: `os_live_<32-char-hex>`.

***

## Response Envelope

All successful responses follow this shape:

```json theme={null}
{
  "data": [...],
  "meta": {
    "count": 142,
    "rate_limit_remaining": 4987
  }
}
```

For single-object responses (e.g. `GET /api/events/{id}`), `data` is an object, not an array.

***

## Error Format

```json theme={null}
{
  "error": {
    "code": "live_not_on_plan",
    "message": "Live odds require the Pro Live plan.",
    "upgrade_url": "https://oddsstream.io/pricing"
  }
}
```

| HTTP Status | Error Code                 | When                                                      |
| ----------- | -------------------------- | --------------------------------------------------------- |
| `400`       | `bad_request`              | Invalid query parameter value                             |
| `401`       | `unauthorized`             | Missing or invalid `X-Api-Key`                            |
| `403`       | `live_not_on_plan`         | Requested `is_live=true` without Pro+ Live plan           |
| `403`       | `websocket_not_on_plan`    | Requested WS token without a plan that includes WebSocket |
| `403`       | `bookmaker_not_configured` | Free plan: no bookmaker selected in dashboard             |
| `403`       | `bookmaker_forbidden`      | Free plan: requesting a different bookmaker than selected |
| `404`       | `not_found`                | Event slug not found in active data                       |
| `409`       | `key_limit_reached`        | Already at the 5-key cap — revoke one first               |
| `429`       | `rate_limit_exceeded`      | Too many requests — see `X-RateLimit-Remaining`           |
| `500`       | `internal_error`           | Server error — safe to retry with backoff                 |
| `503`       | `service_unavailable`      | Database temporarily unavailable                          |

***

## Rate Limits

Limits are per API key:

| Plan      | Rate limit   | WebSocket          | Live endpoints |
| --------- | ------------ | ------------------ | -------------- |
| Free      | 200 req/day  | 30 connections/day | No             |
| Pro       | 5,500 req/hr | Not included       | No             |
| Pro+ RT   | 5,500 req/hr | Unlimited          | No             |
| Pro+ Live | 5,500 req/hr | Unlimited          | Yes            |

Rate limit headers on every response:

```
X-RateLimit-Remaining: 187
X-RateLimit-Reset: 1745160060
```

***

## Common Query Parameters

These filters are accepted on all list endpoints unless stated otherwise:

| Parameter     | Type    | Description                                                                       |
| ------------- | ------- | --------------------------------------------------------------------------------- |
| `sport`       | string  | Filter by sport name — case-insensitive (e.g. `Football`, `Tennis`, `Basketball`) |
| `competition` | string  | Filter by competition code — exact, uppercase (e.g. `EPL`, `NBA`, `LIG1`)         |
| `bookmaker`   | string  | Filter by bookmaker name — exact, case-sensitive (e.g. `Winamax`, `Betclic`)      |
| `limit`       | integer | Max results returned. Each endpoint has its own cap.                              |

***

## Competition Codes

Competition codes are short uppercase identifiers used across all endpoints. They are stable — they don't change with bookmaker translations.

Examples:

| Code   | Competition             | Sport            |
| ------ | ----------------------- | ---------------- |
| `EPL`  | English Premier League  | Football         |
| `LIG1` | Ligue 1                 | Football         |
| `BUND` | Bundesliga              | Football         |
| `SERA` | Serie A                 | Football         |
| `LIGA` | La Liga                 | Football         |
| `UCL`  | UEFA Champions League   | Football         |
| `NBA`  | NBA                     | Basketball       |
| `NFL`  | NFL                     | AmericanFootball |
| `NHL`  | NHL                     | Hockey           |
| `MLB`  | MLB                     | Baseball         |
| `ATP`  | ATP Tour                | Tennis           |
| `WTA`  | WTA Tour                | Tennis           |
| `EFL`  | English Football League | Football         |
| `ERED` | Eredivisie              | Football         |
| `MLS`  | MLS                     | Football         |

Use `GET /api/sports` to discover all active competition codes at runtime.

***

## Market Types

| `market_type`    | Description                           | Sports                       |
| ---------------- | ------------------------------------- | ---------------------------- |
| `moneyline`      | Win/Draw/Win (1X2) or Head-to-Head    | All                          |
| `spread`         | Asian Handicap / Points Spread        | Football, Basketball, Hockey |
| `total`          | Over/Under totals                     | All                          |
| `btts`           | Both Teams to Score (Yes/No)          | Football                     |
| `double_chance`  | Double Chance (1X, X2, 12)            | Football                     |
| `draw_no_bet`    | Draw No Bet (Home/Away)               | Football                     |
| `3way_handicap`  | European Handicap (3-way)             | Football                     |
| `total_games`    | Total Games Over/Under                | Tennis                       |
| `games_handicap` | Games Handicap                        | Tennis                       |
| `team_total`     | Per-team total (Home/Away Over/Under) | Football, Basketball         |
| `player_prop`    | Player performance props              | Basketball, Hockey, Baseball |
| `yes_no`         | Binary Yes/No markets                 | Various                      |

***

## Period Values

The `period` field on every odds row indicates which portion of the match the market covers:

| `period` | Football  | Basketball  | Hockey     | Tennis  |
| -------- | --------- | ----------- | ---------- | ------- |
| `0`      | Full time | Full game   | Full game  | Match   |
| `1`      | 1st Half  | 1st Quarter | 1st Period | 1st Set |
| `2`      | 2nd Half  | 2nd Quarter | 2nd Period | 2nd Set |
| `3`      | —         | 3rd Quarter | 3rd Period | 3rd Set |

Full-time (`period: 0`) markets dominate. Half-time and quarter markets are available for most sports.
