# Sports API (v1.0.0)

REST API for football data across upcoming and historical matches (scores, stats, xG, and odds where available).

## Base URL

- `https://api.thestatsapi.com/api` — Production server

## Authentication

All endpoints require a **Bearer token** in the `Authorization` header unless marked otherwise.

```
Authorization: Bearer YOUR_API_KEY
```

---

## Endpoints

### Health
API health check

#### GET `/health`
**API health check**

**Response (200):**

```json
{
  "status": "healthy",
  "timestamp": <string (date-time)>
}
```

### Competitions
Football competitions/leagues

#### GET `/football/competitions`
**List all competitions**

Retrieve a paginated list of football competitions

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `page` | integer | No | `1` | Page number (default 1) |
| `per_page` | integer | No | `20` | Results per page (default 20, max 100) |
| `country` | string | No |  | Filter by country name (optional) |
| `country_code` | string | No |  | Filter by country code (optional) |
| `type` | string — enum: `league`, `cup`, `tournament` | No |  | Filter by competition type (optional). Values: `league`, `cup`, `tournament` |
| `search` | string | No |  | Search competitions by name (case-insensitive partial match). Example: `Premier` |

**Response (200):**

```json
{
  "data": [
    {
      "id": "comp_3879",
      "name": "Premier League",
      "country": "England",
      "country_code": "GB",
      "type": "league",
      "has_team_stats": true,
      "has_player_stats": true,
      "live_odds_available": true,
      "xg_available": true
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 100,
    "total_pages": 5
  }
}
```

#### GET `/football/competitions/{competition_id}`
**Get competition details**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `competition_id` | string | Yes | Example: `comp_3879` |

**Response (200):**

```json
{
  "data": {
    "id": "comp_3879",
    "name": "Premier League",
    "country": "England",
    "country_code": "GB",
    "type": "league",
    "has_team_stats": true,
    "has_player_stats": true,
    "live_odds_available": true,
    "xg_available": true,
    "current_season_id": "sn_7210",
    "total_teams": 20
  }
}
```

#### GET `/football/competitions/{competition_id}/seasons`
**List seasons for a competition**

Returns all seasons for the competition, newest first. The season with `is_current` true matches `current_season_id` on the competition detail response.

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `competition_id` | string | Yes | Example: `comp_3879` |

**Response (200):**

```json
{
  "data": [
    {
      "id": "sn_7210",
      "name": "Premier League 24/25",
      "year": "2024",
      "is_current": <boolean>
    }
  ]
}
```

### Teams
Football teams

#### GET `/football/teams`
**List teams**

Retrieve a paginated list of teams with optional filters

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `page` | integer | No | `1` | Page number (default 1) |
| `per_page` | integer | No | `20` | Results per page (default 20, max 100) |
| `competition_id` | string | No |  | Filter teams by competition (optional). Example: `comp_3879` |
| `season_id` | string | No |  | Filter teams by season (optional, requires competition_id). Example: `sn_7210` |
| `country` | string | No |  | Filter teams by country (optional) |
| `search` | string | No |  | Search teams by name (case-insensitive partial match). Example: `Arsenal` |

**Response (200):**

```json
{
  "data": [
    {
      "id": "tm_8923",
      "name": "Manchester United",
      "short_name": "Man Utd",
      "country": "England",
      "primary_competition": {
        "id": "comp_17",
        "name": "Premier League"
      }
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 100,
    "total_pages": 5
  }
}
```

#### GET `/football/teams/{team_id}/players`
**Get full team squad**

Returns all players assigned to the club (players.teamid), up to 100, with extended profile fields. Omits deceased and usercount.

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `team_id` | string | Yes | Example: `tm_8923` |

**Response (200):**

```json
{
  "data": [
    {
      "id": "pl_6241",
      "name": "Bruno Fernandes",
      "first_name": "Bruno",
      "last_name": "Fernandes",
      "position": "Midfielder",
      "date_of_birth": "1994-09-08T00:00:00.000Z",
      "age": 29,
      "nationality": "Portugal",
      "height_cm": 179,
      "current_team": {
        "id": "tm_8923",
        "name": "Manchester United",
        "jersey_number": 18
      },
      "slug": "bruno-fernandes",
      "short_name": "B. Fernandes",
      "preferred_foot": "Right",
      "gender": <string | null>,
      "country_slug": <string | null>,
      "market_value": <number | null>,
      "contract_until": <string (date) | null>,
      "national_team": {
        "id": "tm_1234",
        "name": "Portugal"
      }
    }
  ]
}
```

#### GET `/football/teams/{team_id}`
**Get team details**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `team_id` | string | Yes | Example: `tm_8923` |

**Response (200):**

```json
{
  "data": {
    "id": "tm_8923",
    "name": "Manchester United",
    "short_name": "Man Utd",
    "country": "England",
    "primary_competition": {
      "id": "comp_17",
      "name": "Premier League"
    },
    "stadium": {
      "name": "Old Trafford",
      "capacity": 74310,
      "city": "Manchester"
    }
  }
}
```

#### GET `/football/teams/{team_id}/stats`
**Get team statistics**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `team_id` | string | Yes | Example: `tm_8923` |

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `season_id` | string | Yes |  | Example: `sn_7210` |

**Response (200):**

```json
{
  "data": {
    "team_id": "tm_8923",
    "season_id": "sn_7210",
    "competition_id": "comp_3879",
    "matches_played": 38,
    "wins": 23,
    "draws": 6,
    "losses": 9,
    "points": 75,
    "position": 3,
    "goals_for": 58,
    "goals_against": 43,
    "goal_difference": 15,
    "form": "WWDLW"
  }
}
```

### Matches
Football matches

#### GET `/football/matches`
**List matches**

Retrieve a paginated list of matches with optional filters. Use date/status filters to query past (e.g. finished) or upcoming fixtures.

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `page` | integer | No | `1` | Page number (default 1) |
| `per_page` | integer | No | `20` | Results per page (default 20, max 100) |
| `competition_id` | string | No |  | Filter by competition (optional). Example: `comp_3879` |
| `season_id` | string | No |  | Filter by season (optional, requires competition_id). Example: `sn_7210` |
| `team_id` | string | No |  | Filter matches involving a specific team (optional). Example: `tm_8923` |
| `date_from` | string (date) | No |  | Filter matches from this date onwards (optional, format YYYY-MM-DD) |
| `date_to` | string (date) | No |  | Filter matches until this date (optional, format YYYY-MM-DD) |
| `utc_offset` | string | No | `+00:00` | UTC offset for date interpretation (e.g. +05:30, -04:00). Defaults to +00:00 (UTC). |
| `status` | string — enum: `scheduled`, `live`, `finished`, `postponed`, `cancelled` | No |  | Filter by match status (optional). Values: `scheduled`, `live`, `finished`, `postponed`, `cancelled` |

**Response (200):**

```json
{
  "data": [
    {
      "id": "mt_14502",
      "competition_id": "comp_3879",
      "season_id": "sn_7210",
      "matchday": 20,
      "status": "finished",
      "utc_date": "2024-01-15T15:00:00.000Z",
      "home_team": {
        "id": "tm_8923",
        "name": "Manchester United"
      },
      "away_team": {
        "id": "tm_5021",
        "name": "Liverpool"
      },
      "score": {
        "home": 2,
        "away": 1
      },
      "live_odds_available": true,
      "xg_available": true
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 100,
    "total_pages": 5
  }
}
```

#### GET `/football/matches/{match_id}`
**Get match details**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Response (200):**

```json
{
  "data": {
    "id": "mt_14502",
    "competition_id": "comp_3879",
    "competition_name": "Premier League",
    "season_id": "sn_7210",
    "matchday": 20,
    "status": "finished",
    "utc_date": "2024-01-15T15:00:00.000Z",
    "home_team": {
      "id": "tm_8923",
      "name": "Manchester United"
    },
    "away_team": {
      "id": "tm_5021",
      "name": "Liverpool"
    },
    "score": {
      "home": 2,
      "away": 1,
      "half_time_home": 1,
      "half_time_away": 0
    },
    "venue": {
      "name": "Old Trafford",
      "city": "Manchester"
    },
    "referee": {
      "id": "ref_4021",
      "name": "Michael Oliver"
    },
    "odds_available": true,
    "live_odds_available": true,
    "xg_available": true
  }
}
```

#### GET `/football/matches/{match_id}/referee`
**Get match referee**

Returns the referee assigned to the match with career summary stats. `referee` is null when no referee is assigned.

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Response (200):**

```json
{
  "data": {
    "match_id": "mt_14502",
    "referee": {
      "id": "ref_4021",
      "name": "Michael Oliver",
      "slug": <string>,
      "country": <string>,
      "country_code": <string | null>,
      "country_slug": <string | null>,
      "career": {
        "games": <integer>,
        "yellow_cards": <integer>,
        "red_cards": <integer>,
        "yellow_red_cards": <integer>
      }
    }
  }
}
```

#### GET `/football/matches/{match_id}/stats`
**Get match statistics**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Response (200):**

```json
{
  "data": {
    "match_id": "mt_14502",
    "overview": {
      "ball_possession": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "expected_goals": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "big_chances": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "total_shots": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "shots_on_target": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "goalkeeper_saves": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "corner_kicks": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "fouls": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "yellow_cards": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "passes": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "accurate_passes": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "tackles": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "free_kicks": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "shots": {
      "total_shots": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "shots_on_target": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "shots_off_target": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "blocked_shots": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "shots_inside_box": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "shots_outside_box": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "hit_woodwork": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "attack": {
      "big_chances_missed": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "touches_in_penalty_area": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "fouled_in_final_third": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "offsides": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "passes": {
      "accurate_passes": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "throw_ins": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "accurate_crosses": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "accurate_long_balls": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "final_third_entries": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "duels": {
      "duels_won_percentage": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "dispossessed": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "dribbles_percentage": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "ground_duels_percentage": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "aerial_duels_percentage": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "defending": {
      "tackles": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "tackles_won_percentage": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "interceptions": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "clearances": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "ball_recoveries": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "goalkeeping": {
      "saves": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "goal_kicks": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "goals_prevented": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      },
      "high_claims": {
        "all": {
          "home": <number>,
          "away": <number>
        },
        "first_half": {
          "home": <number>,
          "away": <number>
        },
        "second_half": {
          "home": <number>,
          "away": <number>
        }
      }
    },
    "np_expected_goals": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  }
}
```

#### GET `/football/matches/{match_id}/live-stats`
**Get live in-match statistics**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Response (200):**

```json
{
  "data": {
    "match_id": "mt_14502",
    "meta": {
      "match_status": "LIVE",
      "elapsed_minutes": 22,
      "home_goals": 0,
      "away_goals": 0,
      "ht_score": null,
      "poll_count": 1
    },
    "stats": {
      "ballPossession": {
        "home": 48,
        "away": 52,
        "group": "Possession"
      },
      "expectedGoals": {
        "home": 0.08,
        "away": 0.17,
        "group": "expectedGoals"
      },
      "cornerKicks": {
        "home": 1,
        "away": 1,
        "group": "TVData"
      },
      "fouls": {
        "home": 2,
        "away": 0,
        "group": "TVData"
      },
      "totalShotsOnGoal": {
        "home": 2,
        "away": 5,
        "group": "Shots"
      },
      "shotsOnGoal": {
        "home": 0,
        "away": 1,
        "group": "Shots"
      },
      "shotsOffTarget": {
        "home": 2,
        "away": 4,
        "group": "Shots"
      },
      "offsides": {
        "home": 1,
        "away": 3,
        "group": "Other"
      },
      "totalTackle": {
        "home": 2,
        "away": 2,
        "group": "Other"
      },
      "goalKicks": {
        "home": 5,
        "away": 0,
        "group": "Other"
      },
      "throwIns": {
        "home": 6,
        "away": 6,
        "group": "Other"
      }
    }
  }
}
```

#### GET `/football/matches/{match_id}/player-stats`
**Get match player statistics**

Retrieve per-player statistics for a specific match, grouped by passing, shooting, defending, and general stats

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `player_ids` | string | No |  | Comma-separated list of player IDs to filter stats for specific players. Example: `pl_6241,pl_8923,pl_5021` |

**Response (200):**

```json
{
  "data": [
    {
      "player_id": "pl_6241",
      "player_name": "Mohamed Salah",
      "team_id": "tm_8923",
      "position": "F",
      "rating": 8.2,
      "minutes_played": 90,
      "started": true,
      "played": true,
      "passing": {
        "total_passes": <integer>,
        "accurate_passes": <integer>,
        "key_passes": <integer>,
        "assists": <integer>,
        "total_crosses": <integer>,
        "accurate_crosses": <integer>,
        "total_long_balls": <integer>,
        "accurate_long_balls": <integer>
      },
      "shooting": {
        "goals": <integer>,
        "total_shots": <integer>,
        "shots_on_target": <integer>,
        "shots_off_target": <integer>,
        "blocked_shots": <integer>,
        "big_chances_created": <integer>,
        "expected_goals": <number | null>,
        "expected_assists": <number | null>,
        "np_expected_goals": <number | null>
      },
      "duels": {
        "duel_won": <integer>,
        "duel_lost": <integer>,
        "aerial_won": <integer>,
        "challenge_lost": <integer>,
        "won_contest": <integer>,
        "dispossessed": <integer>
      },
      "defending": {
        "tackles": <integer>,
        "interceptions": <integer>,
        "clearances": <integer>
      },
      "goalkeeping": {
        "saves": <integer>
      },
      "general": {
        "touches": <integer>,
        "fouls": <integer>,
        "was_fouled": <integer>,
        "offsides": <integer>,
        "yellow_cards": <integer>,
        "red_cards": <integer>,
        "possession_lost": <integer>,
        "player_subbed_on": "pl_57344297",
        "player_subbed_off": "pl_6241"
      }
    }
  ]
}
```

#### GET `/football/matches/{match_id}/shotmap`
**Get match shotmap**

Retrieve every shot taken in a match, along with per-shot metadata (xG, body part, situation, coordinates, derived flags) and an aggregated non-penalty xG summary for both teams.

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `player_id` | string | No |  | Filter the shot list to a single player. Example: `pl_6241` |

**Response (200):**

```json
{
  "match_id": "mt_14502",
  "event": {
    "id": "mt_14502",
    "home_team_id": "tm_8923",
    "away_team_id": "tm_4512"
  },
  "data": [
    {
      "id": "sh_4812",
      "player_id": "pl_6241",
      "player_name": "Mohamed Salah",
      "team_id": "tm_8923",
      "team_name": "Liverpool",
      "x": 62.1,
      "y": 88.3,
      "minute": 24,
      "result": "goal",
      "expected_goals": 0.47,
      "situation": "regular",
      "body_part": "right-foot",
      "goal_type": "regular",
      "goal_mouth_location": "high-left",
      "goal_mouth_coordinates": {
        "x": <number>,
        "y": <number>,
        "z": <number>
      },
      "block_coordinates": {
        "x": <number>,
        "y": <number>,
        "z": <number>
      },
      "is_blocked_shot": <boolean>,
      "blocked_by_player_id": "pl_5021",
      "goalkeeper": {
        "id": "pl_8959",
        "name": "Manuel Neuer"
      },
      "is_goal": <boolean>,
      "is_on_target": <boolean>,
      "is_headed": <boolean>,
      "is_outside_box": <boolean>,
      "is_penalty": <boolean>
    }
  ],
  "np_xg_summary": {
    "live": {
      "home_team": 1.45,
      "away_team": 0.82
    },
    "stored": {
      "home_team": 1.45,
      "away_team": 0.82
    }
  }
}
```

### Players
Football players

#### GET `/football/players`
**List players**

Retrieve a paginated list of players with optional filters

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `page` | integer | No | `1` | Page number (default 1) |
| `per_page` | integer | No | `20` | Results per page (default 20, max 100) |
| `team_id` | string | No |  | Filter by team (optional). Example: `tm_8923` |
| `position` | string | No |  | Filter by position (optional) |
| `search` | string | No |  | Search players by name (case-insensitive partial match). Example: `Salah` |
| `player_ids` | string | No |  | Comma-separated list of player IDs to fetch specific players. Example: `pl_6241,pl_8923,pl_5021` |

**Response (200):**

```json
{
  "data": [
    {
      "id": "pl_6241",
      "name": "Bruno Fernandes",
      "first_name": "Bruno",
      "last_name": "Fernandes",
      "position": "Midfielder",
      "date_of_birth": "1994-09-08T00:00:00.000Z",
      "age": 29,
      "nationality": "Portugal",
      "height_cm": 179,
      "current_team": {
        "id": "tm_8923",
        "name": "Manchester United",
        "jersey_number": 18
      }
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 100,
    "total_pages": 5
  }
}
```

#### GET `/football/players/{player_id}`
**Get player details**

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `player_id` | string | Yes | Example: `pl_6241` |

**Response (200):**

```json
{
  "data": {
    "id": "pl_6241",
    "name": "Bruno Fernandes",
    "first_name": "Bruno",
    "last_name": "Fernandes",
    "position": "Midfielder",
    "date_of_birth": "1994-09-08T00:00:00.000Z",
    "age": 29,
    "nationality": "Portugal",
    "height_cm": 179,
    "current_team": {
      "id": "tm_8923",
      "name": "Manchester United",
      "jersey_number": 18
    }
  }
}
```

#### GET `/football/players/{player_id}/stats`
**Get player statistics**

Retrieve statistics for a player in a specific season

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `player_id` | string | Yes | Unique identifier for the player. Example: `pl_6241` |

**Query parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `season_id` | string | Yes |  | Season identifier (required). Example: `sn_7210` |
| `competition_id` | string | No |  | Filter statistics by competition (optional). Example: `comp_3879` |

**Response (200):**

```json
{
  "data": {
    "player_id": "pl_6241",
    "season_id": "sn_7210",
    "team_id": "tm_8923",
    "position": "M",
    "rating": 7.24,
    "appearances": 37,
    "starts": 35,
    "minutes_played": 3120,
    "scoring": {
      "goals": <integer>,
      "assists": <integer>,
      "goals_assists_sum": <integer>,
      "goal_conversion_percentage": <number>,
      "penalties_taken": <integer>,
      "penalty_goals": <integer>,
      "big_chances_created": <integer>,
      "big_chances_missed": <integer>
    },
    "shooting": {
      "total_shots": <integer>,
      "shots_on_target": <integer>,
      "shots_off_target": <integer>
    },
    "passing": {
      "total_passes": <integer>,
      "accurate_passes": <integer>,
      "inaccurate_passes": <integer>,
      "pass_accuracy": <number>,
      "key_passes": <integer>,
      "accurate_crosses": <integer>,
      "accurate_crosses_percentage": <number>,
      "accurate_own_half_passes": <integer>,
      "accurate_opposition_half_passes": <integer>,
      "accurate_final_third_passes": <integer>
    },
    "defending": {
      "tackles": <integer>,
      "interceptions": <integer>
    },
    "duels": {
      "ground_duels_won": <integer>,
      "ground_duels_won_percentage": <number>,
      "aerial_duels_won": <integer>,
      "aerial_duels_won_percentage": <number>,
      "total_duels_won": <integer>,
      "total_duels_won_percentage": <number>,
      "successful_dribbles": <integer>,
      "successful_dribbles_percentage": <number>
    },
    "discipline": {
      "yellow_cards": <integer>,
      "red_cards": <integer>,
      "direct_red_cards": <integer>,
      "penalty_won": <integer>,
      "penalty_conceded": <integer>
    }
  }
}
```

### Odds
Betting odds for football matches

#### GET `/football/matches/{match_id}/odds`
**Get match odds**

Retrieve odds for a match across all key markets (Match Result, BTTS, Total Goals, Corners, Asian Handicap) with opening and last-seen values. Available for upcoming and finished matches where odds were captured.

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Response (200):**

```json
{
  "data": {
    "match_id": "mt_14502",
    "bookmakers": [
      {
        "bookmaker": "Pinnacle",
        "markets": {
          "match_odds": {
            "home": {
              "opening": "1.750",
              "last_seen": "1.810"
            },
            "draw": {
              "opening": "1.750",
              "last_seen": "1.810"
            },
            "away": {
              "opening": "1.750",
              "last_seen": "1.810"
            }
          },
          "btts": {
            "yes": {
              "opening": "1.750",
              "last_seen": "1.810"
            },
            "no": {
              "opening": "1.750",
              "last_seen": "1.810"
            }
          },
          "total_goals": {
            "<key>": {
              "over": { ... },
              "under": { ... }
            }
          },
          "match_corners": {
            "<key>": {
              "over": { ... },
              "under": { ... }
            }
          },
          "asian_handicap": {
            "home": {
              "-0.5": {
                "opening": "3.130",
                "last_seen": "3.780"
              },
              "+0.0": {
                "opening": "2.250",
                "last_seen": "2.720"
              }
            },
            "away": {
              "+0.5": {
                "opening": "1.390",
                "last_seen": "1.290"
              },
              "+0.0": {
                "opening": "1.690",
                "last_seen": "1.500"
              }
            }
          }
        }
      }
    ]
  }
}
```

#### GET `/football/matches/{match_id}/odds/live`
**Get live match odds**

Retrieve in-play odds for a match across all key markets (Match Result, BTTS, Total Goals, Corners, Asian Handicap) with last-seen values

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `match_id` | string | Yes | Example: `mt_14502` |

**Response (200):**

```json
{
  "data": {
    "match_id": "mt_14502",
    "bookmakers": [
      {
        "bookmaker": "Bet365",
        "markets": {
          "match_odds": {
            "home": {
              "live": "1.810"
            },
            "draw": {
              "live": "1.810"
            },
            "away": {
              "live": "1.810"
            }
          },
          "btts": {
            "yes": {
              "live": "1.810"
            },
            "no": {
              "live": "1.810"
            }
          },
          "total_goals": {
            "<key>": {
              "over": { ... },
              "under": { ... }
            }
          },
          "match_corners": {
            "<key>": {
              "over": { ... },
              "under": { ... }
            }
          },
          "asian_handicap": {
            "home": {
              "-0.5": {
                "live": "2.670"
              },
              "+0.0": {
                "live": "1.750"
              }
            },
            "away": {
              "+0.5": {
                "live": "1.450"
              },
              "+0.0": {
                "live": "2.050"
              }
            }
          }
        }
      }
    ]
  }
}
```

---

## Models

### Competition

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `comp_3879` |
| `name` | string | Yes | e.g. `Premier League` |
| `country` | string | Yes | e.g. `England` |
| `country_code` | string | Yes | e.g. `GB` |
| `type` | string — enum: `league`, `cup`, `tournament` | Yes | Values: `league`, `cup`, `tournament`. e.g. `league` |
| `has_team_stats` | boolean | Yes | Indicates if team statistics are available for this competition |
| `has_player_stats` | boolean | Yes | Indicates if player statistics are available for this competition |
| `live_odds_available` | boolean | No | Indicates if live (in-play) odds are currently available for at least one match in this competition |
| `xg_available` | boolean | No | Indicates if expected goals (xG) data is available for this competition |

**Example:**

```json
{
  "id": "comp_3879",
  "name": "Premier League",
  "country": "England",
  "country_code": "GB",
  "type": "league",
  "has_team_stats": true,
  "has_player_stats": true,
  "live_odds_available": true,
  "xg_available": true
}
```

### CompetitionDetail

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `comp_3879` |
| `name` | string | Yes | e.g. `Premier League` |
| `country` | string | Yes | e.g. `England` |
| `country_code` | string | Yes | e.g. `GB` |
| `type` | string — enum: `league`, `cup`, `tournament` | Yes | Values: `league`, `cup`, `tournament`. e.g. `league` |
| `has_team_stats` | boolean | Yes | Indicates if team statistics are available for this competition |
| `has_player_stats` | boolean | Yes | Indicates if player statistics are available for this competition |
| `live_odds_available` | boolean | No | Indicates if live (in-play) odds are currently available for at least one match in this competition |
| `xg_available` | boolean | No | Indicates if expected goals (xG) data is available for this competition |
| `current_season_id` | string | No | e.g. `sn_7210` |
| `total_teams` | integer | No | e.g. `20` |

**Example:**

```json
{
  "id": "comp_3879",
  "name": "Premier League",
  "country": "England",
  "country_code": "GB",
  "type": "league",
  "has_team_stats": true,
  "has_player_stats": true,
  "live_odds_available": true,
  "xg_available": true,
  "current_season_id": "sn_7210",
  "total_teams": 20
}
```

### CompetitionSeason

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `sn_7210` |
| `name` | string | Yes | e.g. `Premier League 24/25` |
| `year` | string | Yes | e.g. `2024` |
| `is_current` | boolean | Yes | True for the latest season row for this competition (same id as `current_season_id` on competition detail). |

**Example:**

```json
{
  "id": "sn_7210",
  "name": "Premier League 24/25",
  "year": "2024",
  "is_current": <boolean>
}
```

### Team

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `tm_8923` |
| `name` | string | Yes | e.g. `Manchester United` |
| `short_name` | string | Yes | e.g. `Man Utd` |
| `country` | string | Yes | e.g. `England` |
| `primary_competition` | object { id, name } \| null | No |  |

**Example:**

```json
{
  "id": "tm_8923",
  "name": "Manchester United",
  "short_name": "Man Utd",
  "country": "England",
  "primary_competition": {
    "id": "comp_17",
    "name": "Premier League"
  }
}
```

### TeamDetail

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `tm_8923` |
| `name` | string | Yes | e.g. `Manchester United` |
| `short_name` | string | Yes | e.g. `Man Utd` |
| `country` | string | Yes | e.g. `England` |
| `primary_competition` | object { id, name } \| null | No |  |
| `stadium` | object { name, capacity, city } | No |  |

**Example:**

```json
{
  "id": "tm_8923",
  "name": "Manchester United",
  "short_name": "Man Utd",
  "country": "England",
  "primary_competition": {
    "id": "comp_17",
    "name": "Premier League"
  },
  "stadium": {
    "name": "Old Trafford",
    "capacity": 74310,
    "city": "Manchester"
  }
}
```

### TeamStats

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `team_id` | string | Yes | e.g. `tm_8923` |
| `season_id` | string | Yes | e.g. `sn_7210` |
| `competition_id` | string | Yes | e.g. `comp_3879` |
| `matches_played` | integer | Yes | e.g. `38` |
| `wins` | integer | Yes | e.g. `23` |
| `draws` | integer | Yes | e.g. `6` |
| `losses` | integer | Yes | e.g. `9` |
| `points` | integer | Yes | e.g. `75` |
| `position` | integer | Yes | e.g. `3` |
| `goals_for` | integer | Yes | e.g. `58` |
| `goals_against` | integer | Yes | e.g. `43` |
| `goal_difference` | integer | Yes | e.g. `15` |
| `form` | string | Yes | e.g. `WWDLW` |

**Example:**

```json
{
  "team_id": "tm_8923",
  "season_id": "sn_7210",
  "competition_id": "comp_3879",
  "matches_played": 38,
  "wins": 23,
  "draws": 6,
  "losses": 9,
  "points": 75,
  "position": 3,
  "goals_for": 58,
  "goals_against": 43,
  "goal_difference": 15,
  "form": "WWDLW"
}
```

### Match

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `mt_14502` |
| `competition_id` | string | Yes | e.g. `comp_3879` |
| `season_id` | string | Yes | e.g. `sn_7210` |
| `matchday` | integer | Yes | e.g. `20` |
| `status` | string — enum: `scheduled`, `live`, `finished`, `postponed`, `cancelled` | Yes | Values: `scheduled`, `live`, `finished`, `postponed`, `cancelled`. e.g. `finished` |
| `utc_date` | string (date-time) | Yes | e.g. `"2024-01-15T15:00:00.000Z"` |
| `home_team` | object { id, name } | Yes |  |
| `away_team` | object { id, name } | Yes |  |
| `score` | object { home, away } | Yes |  |
| `live_odds_available` | boolean | No | Indicates if live (in-play) odds are currently available for this match |
| `xg_available` | boolean | No | Indicates if expected goals (xG) data is available for this match |

**Example:**

```json
{
  "id": "mt_14502",
  "competition_id": "comp_3879",
  "season_id": "sn_7210",
  "matchday": 20,
  "status": "finished",
  "utc_date": "2024-01-15T15:00:00.000Z",
  "home_team": {
    "id": "tm_8923",
    "name": "Manchester United"
  },
  "away_team": {
    "id": "tm_5021",
    "name": "Liverpool"
  },
  "score": {
    "home": 2,
    "away": 1
  },
  "live_odds_available": true,
  "xg_available": true
}
```

### MatchDetail

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | No | e.g. `mt_14502` |
| `competition_id` | string | No | e.g. `comp_3879` |
| `competition_name` | string | No | e.g. `Premier League` |
| `season_id` | string | No | e.g. `sn_7210` |
| `matchday` | integer | No | e.g. `20` |
| `status` | string — enum: `scheduled`, `live`, `finished`, `postponed`, `cancelled` | No | Values: `scheduled`, `live`, `finished`, `postponed`, `cancelled`. e.g. `finished` |
| `utc_date` | string (date-time) | No | e.g. `"2024-01-15T15:00:00.000Z"` |
| `home_team` | object { id, name } | No |  |
| `away_team` | object { id, name } | No |  |
| `score` | object { home, away, half_time_home, half_time_away } | No |  |
| `venue` | object { name, city } | No |  |
| `referee` | object { id, name } | No |  |
| `odds_available` | boolean | No | Indicates if betting odds are available for this match (prematch and historical closing lines where stored; supported competitions) |
| `live_odds_available` | boolean | No | Indicates if live (in-play) odds are currently available for this match |
| `xg_available` | boolean | No | Indicates if expected goals (xG) data is available for this match |

**Example:**

```json
{
  "id": "mt_14502",
  "competition_id": "comp_3879",
  "competition_name": "Premier League",
  "season_id": "sn_7210",
  "matchday": 20,
  "status": "finished",
  "utc_date": "2024-01-15T15:00:00.000Z",
  "home_team": {
    "id": "tm_8923",
    "name": "Manchester United"
  },
  "away_team": {
    "id": "tm_5021",
    "name": "Liverpool"
  },
  "score": {
    "home": 2,
    "away": 1,
    "half_time_home": 1,
    "half_time_away": 0
  },
  "venue": {
    "name": "Old Trafford",
    "city": "Manchester"
  },
  "referee": {
    "id": "ref_4021",
    "name": "Michael Oliver"
  },
  "odds_available": true,
  "live_odds_available": true,
  "xg_available": true
}
```

### MatchRefereeResponse

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_id` | string | Yes | e.g. `mt_14502` |
| `referee` | object { id, name, slug, country, country_code, country_slug, career } \| null | Yes | Null when no referee is assigned to the match |

**Example:**

```json
{
  "match_id": "mt_14502",
  "referee": {
    "id": "ref_4021",
    "name": "Michael Oliver",
    "slug": <string>,
    "country": <string>,
    "country_code": <string | null>,
    "country_slug": <string | null>,
    "career": {
      "games": <integer>,
      "yellow_cards": <integer>,
      "red_cards": <integer>,
      "yellow_red_cards": <integer>
    }
  }
}
```

### StatValue

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `home` | number | No |  |
| `away` | number | No |  |

**Example:**

```json
{
  "home": <number>,
  "away": <number>
}
```

### MatchStatItem

Stat with full match and half-by-half breakdown

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `all` | [StatValue](#statvalue) | No |  |
| `first_half` | [StatValue](#statvalue) | No |  |
| `second_half` | [StatValue](#statvalue) | No |  |

**Example:**

```json
{
  "all": {
    "home": <number>,
    "away": <number>
  },
  "first_half": {
    "home": <number>,
    "away": <number>
  },
  "second_half": {
    "home": <number>,
    "away": <number>
  }
}
```

### MatchStats

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_id` | string | No | e.g. `mt_14502` |
| `overview` | object { ball_possession, expected_goals, big_chances, total_shots, shots_on_target, goalkeeper_saves, corner_kicks, fouls, yellow_cards, passes, accurate_passes, tackles, free_kicks } | No | Key match overview statistics |
| `shots` | object { total_shots, shots_on_target, shots_off_target, blocked_shots, shots_inside_box, shots_outside_box, hit_woodwork } | No | Detailed shot statistics |
| `attack` | object { big_chances_missed, touches_in_penalty_area, fouled_in_final_third, offsides } | No | Attacking statistics |
| `passes` | object { accurate_passes, throw_ins, accurate_crosses, accurate_long_balls, final_third_entries } | No | Passing statistics |
| `duels` | object { duels_won_percentage, dispossessed, dribbles_percentage, ground_duels_percentage, aerial_duels_percentage } | No | Duel statistics |
| `defending` | object { tackles, tackles_won_percentage, interceptions, clearances, ball_recoveries } | No | Defending statistics |
| `goalkeeping` | object { saves, goal_kicks, goals_prevented, high_claims } | No | Goalkeeping statistics |
| `np_expected_goals` | [MatchStatItem](#matchstatitem) | No |  |

**Example:**

```json
{
  "match_id": "mt_14502",
  "overview": {
    "ball_possession": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "expected_goals": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "big_chances": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "total_shots": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "shots_on_target": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "goalkeeper_saves": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "corner_kicks": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "fouls": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "yellow_cards": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "passes": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "accurate_passes": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "tackles": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "free_kicks": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "shots": {
    "total_shots": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "shots_on_target": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "shots_off_target": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "blocked_shots": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "shots_inside_box": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "shots_outside_box": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "hit_woodwork": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "attack": {
    "big_chances_missed": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "touches_in_penalty_area": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "fouled_in_final_third": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "offsides": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "passes": {
    "accurate_passes": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "throw_ins": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "accurate_crosses": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "accurate_long_balls": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "final_third_entries": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "duels": {
    "duels_won_percentage": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "dispossessed": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "dribbles_percentage": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "ground_duels_percentage": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "aerial_duels_percentage": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "defending": {
    "tackles": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "tackles_won_percentage": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "interceptions": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "clearances": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "ball_recoveries": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "goalkeeping": {
    "saves": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "goal_kicks": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "goals_prevented": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    },
    "high_claims": {
      "all": {
        "home": <number>,
        "away": <number>
      },
      "first_half": {
        "home": <number>,
        "away": <number>
      },
      "second_half": {
        "home": <number>,
        "away": <number>
      }
    }
  },
  "np_expected_goals": {
    "all": {
      "home": <number>,
      "away": <number>
    },
    "first_half": {
      "home": <number>,
      "away": <number>
    },
    "second_half": {
      "home": <number>,
      "away": <number>
    }
  }
}
```

### LiveMatchStatItem

Single live in-match statistic with home/away tally and group bucket.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `home` | number | No | e.g. `5` |
| `away` | number | No | e.g. `8` |
| `group` | string | No | Provider-side grouping bucket (e.g. "Possession", "Shots", "TVData", "Other", "expectedGoals"). |

**Example:**

```json
{
  "home": 5,
  "away": 8,
  "group": "Shots"
}
```

### LiveMatchStatsMeta

Provider context describing when/how the live tick was sampled.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_status` | string \| null | No | Provider-reported status when the row was sampled (e.g. "LIVE", "FT"). |
| `elapsed_minutes` | integer \| null | No | e.g. `22` |
| `home_goals` | integer \| null | No | e.g. `0` |
| `away_goals` | integer \| null | No | e.g. `0` |
| `ht_score` | string \| null | No | e.g. `null` |
| `poll_count` | integer \| null | No | How many live ticks have updated this row since insertion. |

**Example:**

```json
{
  "match_status": "LIVE",
  "elapsed_minutes": 22,
  "home_goals": 0,
  "away_goals": 0,
  "ht_score": null,
  "poll_count": 1
}
```

### LiveMatchStats

In-match live statistics for a fixture. Returned from `live_event_statistics`, which is populated by realtime providers (OddAlerts, etc.) and kept separate from post-match canonical totals.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_id` | string | No | e.g. `mt_14502` |
| `meta` | [LiveMatchStatsMeta](#livematchstatsmeta) | No |  |
| `stats` | Map<string, [LiveMatchStatItem](#livematchstatitem)> | No | All live stats for the match, keyed by `statistic_key`. Always reflects the running full-match tally (period = `ALL`). |

**Example:**

```json
{
  "match_id": "mt_14502",
  "meta": {
    "match_status": "LIVE",
    "elapsed_minutes": 22,
    "home_goals": 0,
    "away_goals": 0,
    "ht_score": null,
    "poll_count": 1
  },
  "stats": {
    "ballPossession": {
      "home": 48,
      "away": 52,
      "group": "Possession"
    },
    "expectedGoals": {
      "home": 0.08,
      "away": 0.17,
      "group": "expectedGoals"
    },
    "cornerKicks": {
      "home": 1,
      "away": 1,
      "group": "TVData"
    },
    "fouls": {
      "home": 2,
      "away": 0,
      "group": "TVData"
    },
    "totalShotsOnGoal": {
      "home": 2,
      "away": 5,
      "group": "Shots"
    },
    "shotsOnGoal": {
      "home": 0,
      "away": 1,
      "group": "Shots"
    },
    "shotsOffTarget": {
      "home": 2,
      "away": 4,
      "group": "Shots"
    },
    "offsides": {
      "home": 1,
      "away": 3,
      "group": "Other"
    },
    "totalTackle": {
      "home": 2,
      "away": 2,
      "group": "Other"
    },
    "goalKicks": {
      "home": 5,
      "away": 0,
      "group": "Other"
    },
    "throwIns": {
      "home": 6,
      "away": 6,
      "group": "Other"
    }
  }
}
```

### MatchPlayerStats

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `player_id` | string | No | e.g. `pl_6241` |
| `player_name` | string | No | e.g. `Mohamed Salah` |
| `team_id` | string | No | e.g. `tm_8923` |
| `position` | string | No | e.g. `F` |
| `rating` | number \| null | No | e.g. `8.2` |
| `minutes_played` | integer | No | e.g. `90` |
| `started` | boolean | No | Whether the player started the match |
| `played` | boolean | No | Whether the player played any minutes |
| `passing` | object { total_passes, accurate_passes, key_passes, assists, total_crosses, accurate_crosses, total_long_balls, accurate_long_balls } | No |  |
| `shooting` | object { goals, total_shots, shots_on_target, shots_off_target, blocked_shots, big_chances_created, expected_goals, expected_assists, np_expected_goals } | No |  |
| `duels` | object { duel_won, duel_lost, aerial_won, challenge_lost, won_contest, dispossessed } | No |  |
| `defending` | object { tackles, interceptions, clearances } | No |  |
| `goalkeeping` | object { saves } | No |  |
| `general` | object { touches, fouls, was_fouled, offsides, yellow_cards, red_cards, possession_lost, player_subbed_on, player_subbed_off } | No |  |

**Example:**

```json
{
  "player_id": "pl_6241",
  "player_name": "Mohamed Salah",
  "team_id": "tm_8923",
  "position": "F",
  "rating": 8.2,
  "minutes_played": 90,
  "started": true,
  "played": true,
  "passing": {
    "total_passes": <integer>,
    "accurate_passes": <integer>,
    "key_passes": <integer>,
    "assists": <integer>,
    "total_crosses": <integer>,
    "accurate_crosses": <integer>,
    "total_long_balls": <integer>,
    "accurate_long_balls": <integer>
  },
  "shooting": {
    "goals": <integer>,
    "total_shots": <integer>,
    "shots_on_target": <integer>,
    "shots_off_target": <integer>,
    "blocked_shots": <integer>,
    "big_chances_created": <integer>,
    "expected_goals": <number | null>,
    "expected_assists": <number | null>,
    "np_expected_goals": <number | null>
  },
  "duels": {
    "duel_won": <integer>,
    "duel_lost": <integer>,
    "aerial_won": <integer>,
    "challenge_lost": <integer>,
    "won_contest": <integer>,
    "dispossessed": <integer>
  },
  "defending": {
    "tackles": <integer>,
    "interceptions": <integer>,
    "clearances": <integer>
  },
  "goalkeeping": {
    "saves": <integer>
  },
  "general": {
    "touches": <integer>,
    "fouls": <integer>,
    "was_fouled": <integer>,
    "offsides": <integer>,
    "yellow_cards": <integer>,
    "red_cards": <integer>,
    "possession_lost": <integer>,
    "player_subbed_on": "pl_57344297",
    "player_subbed_off": "pl_6241"
  }
}
```

### ShotCoordinates

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `x` | number | No |  |
| `y` | number | No |  |
| `z` | number | No |  |

**Example:**

```json
{
  "x": <number>,
  "y": <number>,
  "z": <number>
}
```

### ShotGoalkeeper

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | No | e.g. `pl_8959` |
| `name` | string | No | e.g. `Manuel Neuer` |

**Example:**

```json
{
  "id": "pl_8959",
  "name": "Manuel Neuer"
}
```

### Shot

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | No | e.g. `sh_4812` |
| `player_id` | string | No | e.g. `pl_6241` |
| `player_name` | string | No | e.g. `Mohamed Salah` |
| `team_id` | string | No | e.g. `tm_8923` |
| `team_name` | string | No | e.g. `Liverpool` |
| `x` | number | No | X coordinate on a 0-100 grid |
| `y` | number | No | Y coordinate on a 0-100 grid (goal at y=100) |
| `minute` | integer | No | e.g. `24` |
| `result` | string | No | Raw result label (goal, saved, miss, block, post, …) |
| `expected_goals` | number \| null | No | Per-shot expected goals (xG) |
| `situation` | string \| null | No | e.g. `regular` |
| `body_part` | string \| null | No | e.g. `right-foot` |
| `goal_type` | string \| null | No | Present only for goals (e.g. regular, own) |
| `goal_mouth_location` | string \| null | No | e.g. `high-left` |
| `goal_mouth_coordinates` | [ShotCoordinates](#shotcoordinates) \| null | No |  |
| `block_coordinates` | [ShotCoordinates](#shotcoordinates) \| null | No |  |
| `is_blocked_shot` | boolean | No |  |
| `blocked_by_player_id` | string \| null | No | e.g. `pl_5021` |
| `goalkeeper` | [ShotGoalkeeper](#shotgoalkeeper) \| null | No |  |
| `is_goal` | boolean | No |  |
| `is_on_target` | boolean | No |  |
| `is_headed` | boolean | No |  |
| `is_outside_box` | boolean | No |  |
| `is_penalty` | boolean | No |  |

**Example:**

```json
{
  "id": "sh_4812",
  "player_id": "pl_6241",
  "player_name": "Mohamed Salah",
  "team_id": "tm_8923",
  "team_name": "Liverpool",
  "x": 62.1,
  "y": 88.3,
  "minute": 24,
  "result": "goal",
  "expected_goals": 0.47,
  "situation": "regular",
  "body_part": "right-foot",
  "goal_type": "regular",
  "goal_mouth_location": "high-left",
  "goal_mouth_coordinates": {
    "x": <number>,
    "y": <number>,
    "z": <number>
  },
  "block_coordinates": {
    "x": <number>,
    "y": <number>,
    "z": <number>
  },
  "is_blocked_shot": <boolean>,
  "blocked_by_player_id": "pl_5021",
  "goalkeeper": {
    "id": "pl_8959",
    "name": "Manuel Neuer"
  },
  "is_goal": <boolean>,
  "is_on_target": <boolean>,
  "is_headed": <boolean>,
  "is_outside_box": <boolean>,
  "is_penalty": <boolean>
}
```

### NpXgTeamSummary

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `home_team` | number | No | e.g. `1.45` |
| `away_team` | number | No | e.g. `0.82` |

**Example:**

```json
{
  "home_team": 1.45,
  "away_team": 0.82
}
```

### ShotmapResponse

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_id` | string | No | e.g. `mt_14502` |
| `event` | object { id, home_team_id, away_team_id } | No |  |
| `data` | [Shot](#shot)[] | No |  |
| `np_xg_summary` | object { live, stored } | No |  |

**Example:**

```json
{
  "match_id": "mt_14502",
  "event": {
    "id": "mt_14502",
    "home_team_id": "tm_8923",
    "away_team_id": "tm_4512"
  },
  "data": [
    {
      "id": "sh_4812",
      "player_id": "pl_6241",
      "player_name": "Mohamed Salah",
      "team_id": "tm_8923",
      "team_name": "Liverpool",
      "x": 62.1,
      "y": 88.3,
      "minute": 24,
      "result": "goal",
      "expected_goals": 0.47,
      "situation": "regular",
      "body_part": "right-foot",
      "goal_type": "regular",
      "goal_mouth_location": "high-left",
      "goal_mouth_coordinates": {
        "x": <number>,
        "y": <number>,
        "z": <number>
      },
      "block_coordinates": {
        "x": <number>,
        "y": <number>,
        "z": <number>
      },
      "is_blocked_shot": <boolean>,
      "blocked_by_player_id": "pl_5021",
      "goalkeeper": {
        "id": "pl_8959",
        "name": "Manuel Neuer"
      },
      "is_goal": <boolean>,
      "is_on_target": <boolean>,
      "is_headed": <boolean>,
      "is_outside_box": <boolean>,
      "is_penalty": <boolean>
    }
  ],
  "np_xg_summary": {
    "live": {
      "home_team": 1.45,
      "away_team": 0.82
    },
    "stored": {
      "home_team": 1.45,
      "away_team": 0.82
    }
  }
}
```

### Player

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `pl_6241` |
| `name` | string | Yes | e.g. `Bruno Fernandes` |
| `first_name` | string | Yes | e.g. `Bruno` |
| `last_name` | string | Yes | e.g. `Fernandes` |
| `position` | string | Yes | e.g. `Midfielder` |
| `date_of_birth` | string (date) \| null | No | Null when date of birth is unknown or placeholder in source data |
| `age` | integer \| null | No | Null when date of birth is unknown |
| `nationality` | string | Yes | e.g. `Portugal` |
| `height_cm` | integer | Yes | e.g. `179` |
| `current_team` | object { id, name, jersey_number } | Yes |  |

**Example:**

```json
{
  "id": "pl_6241",
  "name": "Bruno Fernandes",
  "first_name": "Bruno",
  "last_name": "Fernandes",
  "position": "Midfielder",
  "date_of_birth": "1994-09-08T00:00:00.000Z",
  "age": 29,
  "nationality": "Portugal",
  "height_cm": 179,
  "current_team": {
    "id": "tm_8923",
    "name": "Manchester United",
    "jersey_number": 18
  }
}
```

### SquadPlayer

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | e.g. `pl_6241` |
| `name` | string | Yes | e.g. `Bruno Fernandes` |
| `first_name` | string | Yes | e.g. `Bruno` |
| `last_name` | string | Yes | e.g. `Fernandes` |
| `position` | string | Yes | e.g. `Midfielder` |
| `date_of_birth` | string (date) \| null | No | Null when date of birth is unknown or placeholder in source data |
| `age` | integer \| null | No | Null when date of birth is unknown |
| `nationality` | string | Yes | e.g. `Portugal` |
| `height_cm` | integer | Yes | e.g. `179` |
| `current_team` | object { id, name, jersey_number } | Yes |  |
| `slug` | string \| null | Yes | e.g. `bruno-fernandes` |
| `short_name` | string \| null | Yes | e.g. `B. Fernandes` |
| `preferred_foot` | string \| null | Yes | e.g. `Right` |
| `gender` | string \| null | Yes |  |
| `country_slug` | string \| null | Yes |  |
| `market_value` | number \| null | Yes | Market value from data provider when available |
| `contract_until` | string (date) \| null | Yes |  |
| `national_team` | object { id, name } \| null | Yes |  |

**Example:**

```json
{
  "id": "pl_6241",
  "name": "Bruno Fernandes",
  "first_name": "Bruno",
  "last_name": "Fernandes",
  "position": "Midfielder",
  "date_of_birth": "1994-09-08T00:00:00.000Z",
  "age": 29,
  "nationality": "Portugal",
  "height_cm": 179,
  "current_team": {
    "id": "tm_8923",
    "name": "Manchester United",
    "jersey_number": 18
  },
  "slug": "bruno-fernandes",
  "short_name": "B. Fernandes",
  "preferred_foot": "Right",
  "gender": <string | null>,
  "country_slug": <string | null>,
  "market_value": <number | null>,
  "contract_until": <string (date) | null>,
  "national_team": {
    "id": "tm_1234",
    "name": "Portugal"
  }
}
```

### PlayerStats

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `player_id` | string | No | e.g. `pl_6241` |
| `season_id` | string | No | e.g. `sn_7210` |
| `team_id` | string | No | e.g. `tm_8923` |
| `position` | string | No | e.g. `M` |
| `rating` | number \| null | No | e.g. `7.24` |
| `appearances` | integer | No | e.g. `37` |
| `starts` | integer | No | e.g. `35` |
| `minutes_played` | integer | No | e.g. `3120` |
| `scoring` | object { goals, assists, goals_assists_sum, goal_conversion_percentage, penalties_taken, penalty_goals, big_chances_created, big_chances_missed } | No |  |
| `shooting` | object { total_shots, shots_on_target, shots_off_target } | No |  |
| `passing` | object { total_passes, accurate_passes, inaccurate_passes, pass_accuracy, key_passes, accurate_crosses, accurate_crosses_percentage, accurate_own_half_passes, accurate_opposition_half_passes, accurate_final_third_passes } | No |  |
| `defending` | object { tackles, interceptions } | No |  |
| `duels` | object { ground_duels_won, ground_duels_won_percentage, aerial_duels_won, aerial_duels_won_percentage, total_duels_won, total_duels_won_percentage, successful_dribbles, successful_dribbles_percentage } | No |  |
| `discipline` | object { yellow_cards, red_cards, direct_red_cards, penalty_won, penalty_conceded } | No |  |

**Example:**

```json
{
  "player_id": "pl_6241",
  "season_id": "sn_7210",
  "team_id": "tm_8923",
  "position": "M",
  "rating": 7.24,
  "appearances": 37,
  "starts": 35,
  "minutes_played": 3120,
  "scoring": {
    "goals": <integer>,
    "assists": <integer>,
    "goals_assists_sum": <integer>,
    "goal_conversion_percentage": <number>,
    "penalties_taken": <integer>,
    "penalty_goals": <integer>,
    "big_chances_created": <integer>,
    "big_chances_missed": <integer>
  },
  "shooting": {
    "total_shots": <integer>,
    "shots_on_target": <integer>,
    "shots_off_target": <integer>
  },
  "passing": {
    "total_passes": <integer>,
    "accurate_passes": <integer>,
    "inaccurate_passes": <integer>,
    "pass_accuracy": <number>,
    "key_passes": <integer>,
    "accurate_crosses": <integer>,
    "accurate_crosses_percentage": <number>,
    "accurate_own_half_passes": <integer>,
    "accurate_opposition_half_passes": <integer>,
    "accurate_final_third_passes": <integer>
  },
  "defending": {
    "tackles": <integer>,
    "interceptions": <integer>
  },
  "duels": {
    "ground_duels_won": <integer>,
    "ground_duels_won_percentage": <number>,
    "aerial_duels_won": <integer>,
    "aerial_duels_won_percentage": <number>,
    "total_duels_won": <integer>,
    "total_duels_won_percentage": <number>,
    "successful_dribbles": <integer>,
    "successful_dribbles_percentage": <number>
  },
  "discipline": {
    "yellow_cards": <integer>,
    "red_cards": <integer>,
    "direct_red_cards": <integer>,
    "penalty_won": <integer>,
    "penalty_conceded": <integer>
  }
}
```

### OddsValue

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `opening` | string \| null | No | e.g. `1.750` |
| `last_seen` | string | No | e.g. `1.810` |

**Example:**

```json
{
  "opening": "1.750",
  "last_seen": "1.810"
}
```

### OverUnderOdds

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `over` | [OddsValue](#oddsvalue) | No |  |
| `under` | [OddsValue](#oddsvalue) | No |  |

**Example:**

```json
{
  "over": {
    "opening": "1.750",
    "last_seen": "1.810"
  },
  "under": {
    "opening": "1.750",
    "last_seen": "1.810"
  }
}
```

### MatchOddsMarkets

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_odds` | object { home, draw, away } | No |  |
| `btts` | object { yes, no } | No |  |
| `total_goals` | Map<string, [OverUnderOdds](#overunderodds)> | No |  |
| `match_corners` | Map<string, [OverUnderOdds](#overunderodds)> | No |  |
| `asian_handicap` | object { home, away } | No |  |

**Example:**

```json
{
  "match_odds": {
    "home": {
      "opening": "1.750",
      "last_seen": "1.810"
    },
    "draw": {
      "opening": "1.750",
      "last_seen": "1.810"
    },
    "away": {
      "opening": "1.750",
      "last_seen": "1.810"
    }
  },
  "btts": {
    "yes": {
      "opening": "1.750",
      "last_seen": "1.810"
    },
    "no": {
      "opening": "1.750",
      "last_seen": "1.810"
    }
  },
  "total_goals": {
    "<key>": {
      "over": {
        "opening": "1.750",
        "last_seen": "1.810"
      },
      "under": {
        "opening": "1.750",
        "last_seen": "1.810"
      }
    }
  },
  "match_corners": {
    "<key>": {
      "over": {
        "opening": "1.750",
        "last_seen": "1.810"
      },
      "under": {
        "opening": "1.750",
        "last_seen": "1.810"
      }
    }
  },
  "asian_handicap": {
    "home": {
      "-0.5": {
        "opening": "3.130",
        "last_seen": "3.780"
      },
      "+0.0": {
        "opening": "2.250",
        "last_seen": "2.720"
      }
    },
    "away": {
      "+0.5": {
        "opening": "1.390",
        "last_seen": "1.290"
      },
      "+0.0": {
        "opening": "1.690",
        "last_seen": "1.500"
      }
    }
  }
}
```

### BookmakerMatchOdds

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `bookmaker` | string | No | e.g. `Pinnacle` |
| `markets` | [MatchOddsMarkets](#matchoddsmarkets) | No |  |

**Example:**

```json
{
  "bookmaker": "Pinnacle",
  "markets": {
    "match_odds": {
      "home": {
        "opening": "1.750",
        "last_seen": "1.810"
      },
      "draw": {
        "opening": "1.750",
        "last_seen": "1.810"
      },
      "away": {
        "opening": "1.750",
        "last_seen": "1.810"
      }
    },
    "btts": {
      "yes": {
        "opening": "1.750",
        "last_seen": "1.810"
      },
      "no": {
        "opening": "1.750",
        "last_seen": "1.810"
      }
    },
    "total_goals": {
      "<key>": {
        "over": {
          "opening": "1.750",
          "last_seen": "1.810"
        },
        "under": {
          "opening": "1.750",
          "last_seen": "1.810"
        }
      }
    },
    "match_corners": {
      "<key>": {
        "over": {
          "opening": "1.750",
          "last_seen": "1.810"
        },
        "under": {
          "opening": "1.750",
          "last_seen": "1.810"
        }
      }
    },
    "asian_handicap": {
      "home": {
        "-0.5": {
          "opening": "3.130",
          "last_seen": "3.780"
        },
        "+0.0": {
          "opening": "2.250",
          "last_seen": "2.720"
        }
      },
      "away": {
        "+0.5": {
          "opening": "1.390",
          "last_seen": "1.290"
        },
        "+0.0": {
          "opening": "1.690",
          "last_seen": "1.500"
        }
      }
    }
  }
}
```

### MatchOdds

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_id` | string | No | e.g. `mt_14502` |
| `bookmakers` | [BookmakerMatchOdds](#bookmakermatchodds)[] | No |  |

**Example:**

```json
{
  "match_id": "mt_14502",
  "bookmakers": [
    {
      "bookmaker": "Pinnacle",
      "markets": {
        "match_odds": {
          "home": {
            "opening": "1.750",
            "last_seen": "1.810"
          },
          "draw": {
            "opening": "1.750",
            "last_seen": "1.810"
          },
          "away": {
            "opening": "1.750",
            "last_seen": "1.810"
          }
        },
        "btts": {
          "yes": {
            "opening": "1.750",
            "last_seen": "1.810"
          },
          "no": {
            "opening": "1.750",
            "last_seen": "1.810"
          }
        },
        "total_goals": {
          "<key>": {
            "over": {
              "opening": "1.750",
              "last_seen": "1.810"
            },
            "under": {
              "opening": "1.750",
              "last_seen": "1.810"
            }
          }
        },
        "match_corners": {
          "<key>": {
            "over": {
              "opening": "1.750",
              "last_seen": "1.810"
            },
            "under": {
              "opening": "1.750",
              "last_seen": "1.810"
            }
          }
        },
        "asian_handicap": {
          "home": {
            "-0.5": {
              "opening": "3.130",
              "last_seen": "3.780"
            },
            "+0.0": {
              "opening": "2.250",
              "last_seen": "2.720"
            }
          },
          "away": {
            "+0.5": {
              "opening": "1.390",
              "last_seen": "1.290"
            },
            "+0.0": {
              "opening": "1.690",
              "last_seen": "1.500"
            }
          }
        }
      }
    }
  ]
}
```

### LiveOddsValue

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `live` | string | No | e.g. `1.810` |

**Example:**

```json
{
  "live": "1.810"
}
```

### LiveOverUnderOdds

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `over` | [LiveOddsValue](#liveoddsvalue) | No |  |
| `under` | [LiveOddsValue](#liveoddsvalue) | No |  |

**Example:**

```json
{
  "over": {
    "live": "1.810"
  },
  "under": {
    "live": "1.810"
  }
}
```

### LiveMatchOddsMarkets

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_odds` | object { home, draw, away } | No |  |
| `btts` | object { yes, no } | No |  |
| `total_goals` | Map<string, [LiveOverUnderOdds](#liveoverunderodds)> | No |  |
| `match_corners` | Map<string, [LiveOverUnderOdds](#liveoverunderodds)> | No |  |
| `asian_handicap` | object { home, away } | No |  |

**Example:**

```json
{
  "match_odds": {
    "home": {
      "live": "1.810"
    },
    "draw": {
      "live": "1.810"
    },
    "away": {
      "live": "1.810"
    }
  },
  "btts": {
    "yes": {
      "live": "1.810"
    },
    "no": {
      "live": "1.810"
    }
  },
  "total_goals": {
    "<key>": {
      "over": {
        "live": "1.810"
      },
      "under": {
        "live": "1.810"
      }
    }
  },
  "match_corners": {
    "<key>": {
      "over": {
        "live": "1.810"
      },
      "under": {
        "live": "1.810"
      }
    }
  },
  "asian_handicap": {
    "home": {
      "-0.5": {
        "live": "2.670"
      },
      "+0.0": {
        "live": "1.750"
      }
    },
    "away": {
      "+0.5": {
        "live": "1.450"
      },
      "+0.0": {
        "live": "2.050"
      }
    }
  }
}
```

### LiveBookmakerMatchOdds

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `bookmaker` | string | No | e.g. `Bet365` |
| `markets` | [LiveMatchOddsMarkets](#livematchoddsmarkets) | No |  |

**Example:**

```json
{
  "bookmaker": "Bet365",
  "markets": {
    "match_odds": {
      "home": {
        "live": "1.810"
      },
      "draw": {
        "live": "1.810"
      },
      "away": {
        "live": "1.810"
      }
    },
    "btts": {
      "yes": {
        "live": "1.810"
      },
      "no": {
        "live": "1.810"
      }
    },
    "total_goals": {
      "<key>": {
        "over": {
          "live": "1.810"
        },
        "under": {
          "live": "1.810"
        }
      }
    },
    "match_corners": {
      "<key>": {
        "over": {
          "live": "1.810"
        },
        "under": {
          "live": "1.810"
        }
      }
    },
    "asian_handicap": {
      "home": {
        "-0.5": {
          "live": "2.670"
        },
        "+0.0": {
          "live": "1.750"
        }
      },
      "away": {
        "+0.5": {
          "live": "1.450"
        },
        "+0.0": {
          "live": "2.050"
        }
      }
    }
  }
}
```

### LiveMatchOdds

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match_id` | string | No | e.g. `mt_14502` |
| `bookmakers` | [LiveBookmakerMatchOdds](#livebookmakermatchodds)[] | No |  |

**Example:**

```json
{
  "match_id": "mt_14502",
  "bookmakers": [
    {
      "bookmaker": "Bet365",
      "markets": {
        "match_odds": {
          "home": {
            "live": "1.810"
          },
          "draw": {
            "live": "1.810"
          },
          "away": {
            "live": "1.810"
          }
        },
        "btts": {
          "yes": {
            "live": "1.810"
          },
          "no": {
            "live": "1.810"
          }
        },
        "total_goals": {
          "<key>": {
            "over": {
              "live": "1.810"
            },
            "under": {
              "live": "1.810"
            }
          }
        },
        "match_corners": {
          "<key>": {
            "over": {
              "live": "1.810"
            },
            "under": {
              "live": "1.810"
            }
          }
        },
        "asian_handicap": {
          "home": {
            "-0.5": {
              "live": "2.670"
            },
            "+0.0": {
              "live": "1.750"
            }
          },
          "away": {
            "+0.5": {
              "live": "1.450"
            },
            "+0.0": {
              "live": "2.050"
            }
          }
        }
      }
    }
  ]
}
```

### PaginationMeta

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `page` | integer | No | e.g. `1` |
| `per_page` | integer | No | e.g. `20` |
| `total` | integer | No | e.g. `100` |
| `total_pages` | integer | No | e.g. `5` |

**Example:**

```json
{
  "page": 1,
  "per_page": 20,
  "total": 100,
  "total_pages": 5
}
```

### Error

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `error` | object { code, message, status_code } | No |  |

**Example:**

```json
{
  "error": {
    "code": "invalid_request",
    "message": "Invalid parameter",
    "status_code": 400
  }
}
```

---

## Error Responses

All errors follow this format:

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "status_code": 400
  }
}
```

| Status | Code | Description |
|--------|------|-------------|
| 400 | `invalid_request` | Bad request / missing required parameter |
| 401 | `unauthorized` | Missing or invalid API key |
| 404 | `not_found` | Resource not found |
