---
name: esports
description: |
  Esports data — Dota 2 (OpenDota) and League of Legends esports (Leaguepedia).
  Pro matches, tournaments, teams, and structured LoL competitive data.
  Use when: user asks about Dota 2 pro matches/teams/leagues, or LoL esports
  tournaments/rosters/results.
  Don't use when: user asks about esports betting/odds — use the `kalshi`
  (get_esports_odds) or `polymarket` (get_esports_events) skills (no keyless
  bookmaker-odds source exists). Don't use for non-Dota/LoL titles (only Dota 2
  and LoL are covered).
license: MIT
metadata:
  author: machina-sports
  version: "0.1.0"
---

# Esports Data

Keyless, public sources. **No API key, no signup.**

- **Dota 2** → OpenDota (`api.opendota.com`). Real JSON. Free tier ~60 req/min.
- **League of Legends esports** → Leaguepedia Cargo (`lol.fandom.com`). Structured
  rows. **CC-BY-SA — attribute Leaguepedia when you reuse the data.**

## CRITICAL

- **Dota 2 only** for match/team/league data (OpenDota). LoL uses Leaguepedia Cargo.
- **Leaguepedia is aggressively rate-limited.** The throttle comes back as an
  *in-body* error (`Leaguepedia API error: ...`) on an HTTP 200 — not a 4xx.
  Results are cached ~30 min; don't hammer it. Queries with `order_by` have
  been answered with that throttle body every time, so avoid `order_by` in
  `lol_cargo_query` and sort the rows yourself.
- **OpenDota has no date filter.** `get_pro_matches` returns the latest pro
  matches (100 per call at most). For older ones pass the response's
  `next_less_than_match_id` as `less_than_match_id`.
- **Match Dota teams on `team_id`, not name.** OpenDota has separate records
  whose names differ only in case (`mouz` 161707 from 2013, `MOUZ` 9338413 now).
- **No odds here.** Bookmaker odds for esports are not available keyless. For
  implied-probability signals, use `kalshi get_esports_odds --game=cs2` or
  `polymarket get_esports_events`.
- **Personal use.** Third-party ToS apply (OpenDota MIT; Leaguepedia CC-BY-SA).

## Quick Start

CLI:
```bash
sports-skills esports get_pro_matches --limit=10
sports-skills esports get_pro_teams --limit=10
sports-skills esports get_leagues --tier=premium
sports-skills esports get_lol_tournaments --region=Korea --limit=5
sports-skills esports lol_cargo_query --tables=Teams --fields=Name,Region --limit=5
```

Python:
```python
from sports_skills import esports

esports.get_pro_matches(limit=10)
esports.get_lol_tournaments(region="Brazil")
```

## Commands

| Command | Description |
|---|---|
| `get_pro_matches` | Latest Dota 2 pro matches, newest first; page back with `less_than_match_id` (OpenDota) |
| `get_leagues` | Dota 2 leagues/tournaments, newest first, filter by tier (OpenDota) |
| `get_pro_teams` | Top Dota 2 teams by rating (OpenDota) |
| `get_match` | Detailed Dota 2 match by id (OpenDota) |
| `get_lol_tournaments` | LoL esports tournaments starting since the 1st of the month two months back, newest first (Leaguepedia) |
| `lol_cargo_query` | Raw Leaguepedia Cargo query (any table/fields) |

## Leaguepedia Cargo reference

`lol_cargo_query` needs `tables` + `fields`. Common tables (fields vary — verify
with a small query first): `Tournaments` (Name, DateStart, DateEnd, League,
Region, Prizepool), `MatchSchedule` (Team1, Team2, Winner, DateTime_UTC, BestOf),
`ScoreboardGames`, `Players`, `Teams`. Full schema:
<https://lol.fandom.com/wiki/Special:CargoTables>. Row keys use the field names
you asked for (`DateTime_UTC`), even though Cargo itself returns them with spaces.

## Troubleshooting

- `Leaguepedia API error: You've exceeded your rate limit` → you're being
  throttled. Wait and retry; the connector caches for ~30 min to help.
- Empty `matches`/`teams` from OpenDota → transient; OpenDota is volunteer-run
  best-effort infra with no SLA. Retry.
