OpenDota APIopendota.com ↗
Access Dota 2 match data, player stats, hero win rates, pro match results, and custom SQL queries via the OpenDota API. 9 endpoints.
What is the OpenDota API?
This API provides access to Dota 2 data across 9 endpoints, covering individual match records, player profiles, hero statistics, and professional match results from OpenDota. The get_match endpoint returns over 15 per-player fields including kills, deaths, assists, gold-per-minute, and item slots for every participant. The get_explorer endpoint lets you run raw SQL SELECT queries against the OpenDota database, returning arbitrary row sets from tables like matches, players, heroes, and leagues.
curl -X GET 'https://api.parse.bot/scraper/49173147-70df-4c2d-bade-d462c1e1cbbb/get_match?match_id=8846649222' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace opendota-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: OpenDota SDK — Dota 2 match data, players, heroes, and pro scene."""
from parse_apis.OpenDota_API import OpenDota, NotFoundError
client = OpenDota()
# Search players by name, get summaries, then drill into full profile
for ps in client.player_summaries.search(query="dendi", limit=3):
print(ps.personaname, ps.account_id, ps.last_match_time)
# Get full player profile and check win/loss record
player = client.players.get(account_id=87278757)
print(player.personaname, player.rank_tier, player.leaderboard_rank)
wl = player.win_loss(date=30)
print(wl.win, wl.lose)
# List hero stats
for hero in client.heros.list(limit=3):
print(hero.localized_name, hero.primary_attr, hero.pro_pick, hero.pro_win)
# Browse recent pro match fixtures, then drill into full match details
fixture = client.fixtures.list(limit=1).first()
if fixture:
print(fixture.radiant_name, fixture.dire_name, fixture.radiant_win)
match = fixture.details.get()
print(match.match_id, match.duration, match.radiant_score, match.dire_score)
for p in match.players[:2]:
print(p.personaname, p.hero_id, p.kills, p.deaths, p.assists)
# List players belonging to a team
for tp in client.team_players.list(team_id=8254400, limit=3):
print(tp.name, tp.account_id, tp.games_played, tp.wins, tp.is_current_team_member)
# Execute a custom SQL query
result = client.query_results.execute(sql="SELECT match_id FROM matches LIMIT 1")
print(result.command, result.rowCount)
# Typed error handling
try:
client.matches.get(match_id=9999999999)
except NotFoundError as exc:
print(f"match not found: {exc}")
print("exercised: player_summaries.search / players.get / win_loss / heros.list / fixtures.list / details.get / team_players.list / query_results.execute / matches.get")
Retrieve comprehensive match data including duration, scores, players, and advanced statistics for a specific match. Each player entry contains hero, items, kills/deaths/assists, gold/xp rates, benchmarks, and more.
| Param | Type | Description |
|---|---|---|
| match_idrequired | integer | Numeric match ID to retrieve data for. |
{
"type": "object",
"fields": {
"players": "array of player objects with detailed stats",
"duration": "integer match duration in seconds",
"match_id": "integer match identifier",
"game_mode": "integer game mode identifier",
"dire_score": "integer total Dire kills",
"lobby_type": "integer lobby type identifier",
"start_time": "integer Unix timestamp of match start",
"radiant_win": "boolean indicating if Radiant team won",
"radiant_score": "integer total Radiant kills"
},
"sample": {
"data": {
"players": [
{
"kills": 5,
"deaths": 8,
"assists": 11,
"hero_id": 9,
"account_id": 118774641,
"xp_per_min": 1618,
"gold_per_min": 866
}
],
"duration": 1702,
"match_id": 7900000001,
"game_mode": 23,
"dire_score": 34,
"lobby_type": 0,
"start_time": 1723833571,
"radiant_win": false,
"radiant_score": 38
},
"status": "success"
}
}About the OpenDota API
Match and Player Data
The get_match endpoint takes a match_id integer and returns a full match record: duration, start_time, game_mode, lobby_type, radiant_win, radiant_score, dire_score, and a players array where each object includes the player's hero choice, item slots, KDA figures, gold-per-minute, XP-per-minute, and benchmark comparisons against other players on the same hero. The get_player endpoint accepts an account_id and returns a profile sub-object (Steam personaname, avatarfull, profileurl, loccountrycode) alongside rank_tier (an integer encoding both the medal and star level) and leaderboard_rank for players in the Immortal bracket.
Filtering and Aggregation
get_player_win_loss computes aggregate win and loss counts for a given account_id and accepts several optional filters: hero_id, game_mode, lobby_type, region, patch, and a date parameter that limits results to the last N days. This makes it straightforward to isolate a player's record on a specific hero in ranked matches over a recent patch window. search_players accepts a name string and returns an array of matching player summaries with account_id, personaname, avatarfull, and last_match_time.
Hero and Pro Scene Coverage
get_hero_stats returns every hero in a single response with id, localized_name, primary_attr, attack_type, roles, pro_pick, pro_win, and pro_ban counts. get_pro_matches returns recent professional matches ordered by match_id descending; pass less_than_match_id to paginate backward. Each entry includes radiant_name, dire_name, league_name, duration, radiant_win, and score fields. get_pro_players lists all registered professional players with their team_name, team_tag, country_code, and last_match_time.
SQL Explorer
get_explorer accepts a SQL SELECT query string and executes it against the OpenDota PostgreSQL database. The response includes rows (an array of result objects whose keys depend on your query), command, and rowCount. This endpoint enables ad-hoc analysis that fixed endpoints can't support — cross-table joins, custom aggregations, and slice-and-dice queries across matches, players, heroes, and leagues.
The OpenDota API is a managed, monitored endpoint for opendota.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when opendota.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official opendota.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Track a player's win rate on a specific hero over the last 30 days using
get_player_win_losswithhero_idanddatefilters - Build a hero tier list by ingesting
pro_pick,pro_win, andpro_banfields fromget_hero_stats - Monitor professional match results in real time by polling
get_pro_matchesand paginating withless_than_match_id - Display a player's rank medal and leaderboard position on a stats dashboard using
rank_tierandleaderboard_rankfromget_player - Reconstruct full match timelines and per-player performance breakdowns from the
playersarray inget_match - Identify all players who have represented a specific team using
get_team_playerswithgames_playedandis_current_team_member - Run custom cohort analysis across the OpenDota database using
get_explorerwith a SQL GROUP BY query
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does OpenDota have an official developer API?+
What does get_explorer return and are there restrictions on the queries it accepts?+
get_explorer accepts a SQL SELECT string and returns rows (an array of objects whose keys match your selected columns), command, and rowCount. Only SELECT queries are supported — no INSERT, UPDATE, or DELETE. Query complexity and execution time are subject to limits imposed by the OpenDota platform, so very large scans or unbounded queries may be rejected or time out.Does the API return full match history for a player?+
get_player), win/loss aggregates (get_player_win_loss), and name search (search_players), but there is no endpoint that returns a list of recent matches for a given account. You can fork this API on Parse and revise it to add a match-history endpoint using the player's account ID.How does pagination work for pro matches?+
get_pro_matches returns results ordered by match_id descending. To retrieve older matches, pass the smallest match_id from your current response as the less_than_match_id parameter in the next request. There is no page-number or cursor parameter.Does the API expose draft order or ban sequences for pro matches?+
get_pro_matches returns team names, league, duration, scores, and the match result, but individual pick and ban sequences are not included in that endpoint's response. You can fork this API on Parse and revise it to pull draft data by cross-referencing a full match record via get_match using the returned match_id.