---
title: Odds & predictions
description: The football odds feed, best-price endpoint, bookmaker list and model predictions — all decimal, all free.
badge: free
---

# Odds & predictions

Free multi-bookmaker football odds plus model forecasts. All prices are
**decimal**. For odds across other sports with tick history, see the
separate [Odds API](/docs/odds-api/) product.

## Odds feed

```
GET /api/v2/odds/
```

One row per event × market × outcome × bookmaker, updated continuously.

| Param | Type | Description |
|---|---|---|
| `event_id` | int | One match |
| `league_id` / `season_id` / `team_id` | int | Scope filters |
| `market` | string | `1x2` · `over_under_15` · `over_under_25` · `over_under_35` · `btts` · `double_chance` · `draw_no_bet` · `total_corners` · `total_red_cards` |
| `outcome` | string | e.g. `home`, `draw`, `away`, `over`, `under`, `yes`, `no` |
| `bookmaker_slug` | string | One bookmaker (see `/bookmakers/`) |
| `is_max_quote` | bool | Only rows that are the current best price for their outcome |
| `movement` | string | `up` · `down` — price drift since previous observation |
| `min_decimal_odds` / `max_decimal_odds` | float | Price band |
| `updated_after` | datetime | Only rows updated since this ISO timestamp |
| `limit` / `offset` | int | Pagination |

```json
{
  "id": 90412331, "event_id": 223510,
  "market": "1x2", "outcome": "home", "outcome_name": "Aldosivi",
  "line": null,
  "bookmaker_slug": "pinnacle", "bookmaker_name": "Pinnacle",
  "decimal_odds": 2.45, "previous_decimal_odds": 2.55,
  "implied_probability": 0.408, "movement": "down",
  "is_max_quote": false, "updated_at": "2026-08-02T14:52:11+00:00"
}
```

Lined markets (`total_corners`, `total_red_cards`) populate `line` (e.g.
`9.5`). Single row: `GET /api/v2/odds/{id}/`.

## Best prices

```
GET /api/v2/odds/best/
```

| Param | Type | Description |
|---|---|---|
| `market` | string | Required in practice — which market to rank |
| `league_id` / `season_id` / `team_id` | int | Scope |
| `date_from` / `date_to` | date | Kickoff window |
| `limit` / `offset` | int | Pagination (max 200) |

Per event, the best available decimal price per outcome with the bookmaker
offering it.

## Bookmakers

```
GET /api/v2/bookmakers/    →  [ { "slug": "pinnacle", "name": "Pinnacle" }, … ]
```

The same slugs work on the [WebSocket](/docs/websocket/football/)
`bookmaker_slug` subscription.

## Per-match shortcuts

On any event: `/api/v2/events/{id}/odds/` (consensus),
`/odds/comparison/` (full grid), `/polymarket/` (prediction-market
probabilities) — see [Events & live scores](/docs/football/events/).

### Polymarket

```
GET /api/v2/events/{id}/polymarket/
```

Implied probabilities (0–1 floats, not decimal odds) per market, plus
trading-signal fields for matches with an active prediction market.

```json
{
  "event_id": 223510,
  "markets": {
    "1x2": { "home": 0.41, "draw": 0.29, "away": 0.30 },
    "btts": { "yes": 0.52, "no": 0.48 },
    "over_under": { "over_25": 0.55, "under_25": 0.45 }
  },
  "liquidity": {
    "volume_24hr": 18420.5, "volume_1wk": 96210.0,
    "volume_1mo": 210500.0, "volume_1yr": 890100.0,
    "open_interest": 42110.0,
    "liquidity_clob": 8900.0, "liquidity_amm": 1200.0
  },
  "pricing": {
    "best_bid": 0.40, "best_ask": 0.42,
    "change_1d": 0.015, "change_1wk": -0.03, "change_1mo": 0.08
  },
  "goalscorers": { "...": "..." },
  "exact_scores": { "...": "..." },
  "updated_at": "2026-08-02T14:52:11+00:00"
}
```

`liquidity` and `pricing` sub-fields are `null` when that trading data isn't
available. The endpoint itself returns **404** for matches with no active
Polymarket market.

## Predictions

```
GET /api/v2/predictions/
GET /api/v2/predictions/{id}/
GET /api/v2/events/{id}/prediction/     # same shape, by event
```

| Param | Type | Description |
|---|---|---|
| `status` | string | Filter by match status |
| `league_id` / `season_id` / `team_id` | int | Scope |
| `date_from` / `date_to` | date | Kickoff window |
| `min_confidence` | float | Minimum model confidence (0–1) |
| `recommended` | bool | Only picks the model flags as value |
| `limit` / `offset` | int | Pagination |

```json
{
  "id": 88123,
  "event": { "id": 223510, "home_team": "Aldosivi", "away_team": "Gimnasia y Esgrima", "...": "..." },
  "markets": {
    "match_result": { "prob_home": 34.1, "prob_draw": 30.2, "prob_away": 35.7, "predicted": "away" },
    "expected_goals": { "home": 1.12, "away": 1.24 },
    "over_under": { "prob_over_15": 71.4, "prob_over_25": 44.8, "prob_over_35": 21.9 },
    "btts": { "prob_yes": 48.5 },
    "score": { "most_likely": "1-1" },
    "draw_no_bet": { "prob_home": 48.9 },
    "corners": { "prob_over_85": 61.0, "prob_over_95": 49.2, "prob_over_105": 37.5 }
  },
  "recommendations": { "favorite": "away", "favorite_prob": 35.7, "over_25": false, "btts": false },
  "model": { "confidence": 0.62, "version": "v1" }
}
```

> **Note:** market probabilities are **0–100**; `model.confidence` is
> **0–1**. This asymmetry is a frozen legacy contract kept for
> compatibility.

Predictions are cached ~2 minutes server-side.
