Dota 2 API
Welcome to the BALLDONTLIE Dota 2 API, the best esports API on the planet. This API contains comprehensive data for professional Dota 2 matches, tournaments, teams, players, heroes, items, and abilities. An API key is required. You can obtain an API key by creating a free account on our website. Read the authentication section to learn how to use the API key.
Take a look at our other APIs.
Join us on discord.
AI-Powered Integration
Using the OpenAPI Specification with AI
Our complete OpenAPI specification allows AI assistants to automatically understand and interact with our API. Simply share the spec URL with your AI assistant and describe what you want to build—the AI will handle the technical implementation.
Getting Started with AI:
- Copy this URL:
https://www.balldontlie.io/openapi.yml - Share it with your preferred AI assistant (ChatGPT, Claude, Gemini, etc.)
- Tell the AI what you want to build (e.g., "Create a dashboard showing OG's match results")
- The AI will read the OpenAPI spec and write the code for you
Example prompts to try:
- "Using the OpenAPI spec at https://www.balldontlie.io/openapi.yml, show me how to get N0tail's career statistics"
- "Read the BALLDONTLIE OpenAPI spec and create a Python script that fetches upcoming Dota 2 matches"
- "Help me understand the available Dota 2 endpoints from this OpenAPI spec: https://www.balldontlie.io/openapi.yml"
This makes it incredibly easy for non-technical users, analysts, and researchers to leverage our esports data without needing to learn programming from scratch.
Account Tiers
There are three different account tiers which provide you access to different types of data. Visit our website to create an account for free.
Paid tiers do not apply across sports. The tier you purchase for Dota 2 will not automatically be applied to other sports. You can purchase the ALL-ACCESS ($299.99/mo) tier to get access to every endpoint for every sport.
Read the table below to see the breakdown.
| Endpoint | Free | ALL-STAR | GOAT |
|---|---|---|---|
| Teams | Yes | Yes | Yes |
| Players | Yes | Yes | Yes |
| Heroes | Yes | Yes | Yes |
| Items | Yes | Yes | Yes |
| Abilities | Yes | Yes | Yes |
| Tournaments | No | Yes | Yes |
| Tournament Teams | No | Yes | Yes |
| Tournament Roster | No | Yes | Yes |
| Hero Stats | No | Yes | Yes |
| Matches | No | No | Yes |
| Match Maps | No | No | Yes |
| Player Match Map Stats | No | No | Yes |
| Team Match Map Stats | No | No | Yes |
| Player Overall Stats | No | No | Yes |
The feature breakdown per tier is shown in the table below.
| Tier | Requests / Min | $USD / mo. |
|---|---|---|
| GOAT | 600 | 39.99 |
| ALL-STAR | 60 | 9.99 |
| Free | 5 | 0 |
Authentication
To authorize, use this code:
curl "api_endpoint_here" -H "Authorization: YOUR_API_KEY"
fetch("api_endpoint_here", {
headers: {
Authorization: "YOUR_API_KEY",
},
});
import requests
response = requests.get(
"api_endpoint_here",
headers={"Authorization": "YOUR_API_KEY"}
)
Make sure to replace
YOUR_API_KEYwith your actual API key.
BALLDONTLIE uses API keys to allow access to the API. You can register for a new API key at our developer portal.
BALLDONTLIE expects for the API key to be included in all API requests to the server in a header that looks like the following:
Authorization: YOUR_API_KEY
Pagination
All paginated endpoints support cursor-based pagination. The API returns a meta object containing pagination information:
{
"data": [...],
"meta": {
"next_cursor": 123,
"per_page": 25
}
}
To fetch the next page, include the cursor query parameter:
curl "https://api.balldontlie.io/dota/v1/teams?cursor=123&per_page=25" \
-H "Authorization: YOUR_API_KEY"
Parameters:
- cursor (optional): The cursor value from the previous response's meta.next_cursor
- per_page (optional): Number of results per page (default: 25, max: 100)
Teams
Get All Teams
curl "https://api.balldontlie.io/dota/v1/teams?per_page=25" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/teams?per_page=25", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/teams",
headers={"Authorization": "YOUR_API_KEY"},
params={"per_page": 25}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 196,
"name": "123 Gaming",
"slug": "123-gaming",
"country_name": "Mongolia",
"region": {
"id": 5,
"name": "Asia"
},
"rank": null,
"total_money": null,
"tour_wins": null
},
{
"id": 1347,
"name": "10 Rounds",
"slug": "10-rounds",
"country_name": null,
"region": null,
"rank": null,
"total_money": null,
"tour_wins": null
}
],
"meta": {
"next_cursor": 2,
"per_page": 25
}
}
This endpoint retrieves all Dota 2 teams.
HTTP Request
GET https://api.balldontlie.io/dota/v1/teams
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| search | null | Search teams by name |
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Players
Get All Players
curl "https://api.balldontlie.io/dota/v1/players?per_page=25" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/players?per_page=25", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/players",
headers={"Authorization": "YOUR_API_KEY"},
params={"per_page": 25}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 2131,
"nickname": "N0tail",
"slug": "bigdaddyn0tail",
"first_name": "Johan",
"last_name": "Sundstein",
"birthday": "1993-10-07",
"country_code": "DK",
"country_name": "Denmark",
"is_coach": false,
"total_prize": 100104,
"team": null
}
],
"meta": {
"next_cursor": 26630,
"per_page": 25
}
}
This endpoint retrieves all Dota 2 players.
HTTP Request
GET https://api.balldontlie.io/dota/v1/players
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| search | null | Search players by nickname, first name, or last name |
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Heroes
Get All Heroes
curl "https://api.balldontlie.io/dota/v1/heroes" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/heroes", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/heroes",
headers={"Authorization": "YOUR_API_KEY"}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 1,
"name": "antimage",
"localized_name": "Anti-Mage"
},
{
"id": 2,
"name": "axe",
"localized_name": "Axe"
},
{
"id": 3,
"name": "bane",
"localized_name": "Bane"
},
{
"id": 4,
"name": "bloodseeker",
"localized_name": "Bloodseeker"
},
{
"id": 5,
"name": "crystal_maiden",
"localized_name": "Crystal Maiden"
}
]
}
This endpoint retrieves all Dota 2 heroes.
HTTP Request
GET https://api.balldontlie.io/dota/v1/heroes
Items
Get All Items
curl "https://api.balldontlie.io/dota/v1/items" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/items", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/items",
headers={"Authorization": "YOUR_API_KEY"}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 1,
"name": "blink"
},
{
"id": 2,
"name": "blades_of_attack"
},
{
"id": 3,
"name": "broadsword"
},
{
"id": 4,
"name": "chainmail"
},
{
"id": 5,
"name": "claymore"
}
]
}
This endpoint retrieves all Dota 2 items.
HTTP Request
GET https://api.balldontlie.io/dota/v1/items
Abilities
Get All Abilities
curl "https://api.balldontlie.io/dota/v1/abilities" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/abilities", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/abilities",
headers={"Authorization": "YOUR_API_KEY"}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 5003,
"name": "antimage_mana_break"
},
{
"id": 5004,
"name": "antimage_blink"
},
{
"id": 5005,
"name": "antimage_counterspell"
},
{
"id": 5006,
"name": "antimage_mana_void"
}
]
}
This endpoint retrieves all Dota 2 abilities.
HTTP Request
GET https://api.balldontlie.io/dota/v1/abilities
Tournaments
Get All Tournaments
curl "https://api.balldontlie.io/dota/v1/tournaments?per_page=25" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/tournaments?per_page=25", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/tournaments",
headers={"Authorization": "YOUR_API_KEY"},
params={"per_page": 25}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 703,
"name": "BLAST Slam IX",
"slug": "blast-slam-ix",
"start_date": "2026-11-17T10:55:00.000Z",
"end_date": "2026-11-29T10:55:00.000Z",
"prize": null,
"event_type": "lan",
"tier": "s",
"status": "upcoming"
},
{
"id": 637,
"name": "BLAST Slam VI Europe Closed Qualifier",
"slug": "blast-slam-vi-europe-closed-qualifier",
"start_date": "2026-01-04T20:00:00.000Z",
"end_date": "2026-01-06T21:30:00.000Z",
"prize": null,
"event_type": "online",
"tier": "a",
"status": "upcoming"
}
],
"meta": {
"next_cursor": 343,
"per_page": 25
}
}
This endpoint retrieves all Dota 2 tournaments.
HTTP Request
GET https://api.balldontlie.io/dota/v1/tournaments
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Tournament Teams
Get Tournament Teams
curl "https://api.balldontlie.io/dota/v1/tournament_teams?tournament_id=703" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/tournament_teams?tournament_id=703", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/tournament_teams",
headers={"Authorization": "YOUR_API_KEY"},
params={"tournament_id": 703}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"tournament_id": 703,
"team": {
"id": 7,
"name": "OG"
}
},
{
"tournament_id": 703,
"team": {
"id": 15,
"name": "Team Secret"
}
}
]
}
This endpoint retrieves all teams participating in a specific tournament.
HTTP Request
GET https://api.balldontlie.io/dota/v1/tournament_teams
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| tournament_id | Yes | The ID of the tournament |
Tournament Roster
Get Tournament Roster
curl "https://api.balldontlie.io/dota/v1/tournament_roster?tournament_id=703&team_id=7" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/tournament_roster?tournament_id=703&team_id=7", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/tournament_roster",
headers={"Authorization": "YOUR_API_KEY"},
params={"tournament_id": 703, "team_id": 7}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"tournament_id": 703,
"team_id": 7,
"player": {
"id": 2131,
"nickname": "N0tail"
}
}
]
}
This endpoint retrieves the roster for a specific team in a tournament.
HTTP Request
GET https://api.balldontlie.io/dota/v1/tournament_roster
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| tournament_id | Yes | The ID of the tournament |
| team_id | Yes | The ID of the team |
Hero Stats
Get Hero Stats
curl "https://api.balldontlie.io/dota/v1/hero_stats" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/hero_stats", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/hero_stats",
headers={"Authorization": "YOUR_API_KEY"}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 106,
"hero": {
"id": 106,
"name": "tiny",
"localized_name": "Tiny"
},
"games_count": 4372,
"picks_count": 4372,
"picks_rate": 0.0224,
"wins_count": 2146,
"win_rate": 0.4909,
"ban_rate": 0.023,
"avg_kills": 3.879,
"avg_deaths": 5.628,
"avg_assists": 11.817,
"kda": 2.79,
"kp": 0.7211,
"game_impact": 0.3943,
"avg_net_worth": 16064,
"avg_gpm": 449,
"avg_xpm": 498,
"avg_damage": 19628,
"avg_last_hits": 154,
"avg_denies": 5,
"avg_heal": 2
}
],
"meta": {
"next_cursor": 107,
"per_page": 25
}
}
This endpoint retrieves hero statistics across all professional games.
HTTP Request
GET https://api.balldontlie.io/dota/v1/hero_stats
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Matches
Get All Matches
curl "https://api.balldontlie.io/dota/v1/matches?per_page=25" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/matches?per_page=25", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/matches",
headers={"Authorization": "YOUR_API_KEY"},
params={"per_page": 25}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 13258,
"slug": "9613d486ec-vs-8aba11c1cd-05-01-2026",
"tournament": {
"id": 637,
"name": "BLAST Slam VI Europe Closed Qualifier",
"slug": "blast-slam-vi-europe-closed-qualifier",
"start_date": "2026-01-04T20:00:00.000Z",
"end_date": "2026-01-06T21:30:00.000Z",
"prize": null,
"tier": "a",
"status": "upcoming"
},
"team1": {
"id": 2133,
"name": "Virtus.pro"
},
"team2": {
"id": 1839,
"name": "Team Falcons"
},
"winner": null,
"team1_score": 0,
"team2_score": 0,
"bo_type": 3,
"status": "upcoming",
"start_date": "2026-01-05T17:00:00.000Z",
"end_date": null
}
],
"meta": {
"next_cursor": 2,
"per_page": 25
}
}
This endpoint retrieves all Dota 2 professional matches.
HTTP Request
GET https://api.balldontlie.io/dota/v1/matches
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| team_ids | null | Filter by team IDs (array) |
| tournament_ids | null | Filter by tournament IDs (array) |
| dates | null | Filter by specific dates (YYYY-MM-DD format, array) |
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Match Maps
Get Match Maps (Games)
curl "https://api.balldontlie.io/dota/v1/match_maps?match_id=13258" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/match_maps?match_id=13258", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/match_maps",
headers={"Authorization": "YOUR_API_KEY"},
params={"match_id": 13258}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 123,
"match_id": 13258,
"game_number": 1,
"winner": {
"id": 2133,
"name": "Virtus.pro"
},
"loser": {
"id": 1839,
"name": "Team Falcons"
},
"duration": 2400,
"status": "finished",
"begin_at": "2026-01-05T17:00:00.000Z",
"end_at": "2026-01-05T17:40:00.000Z"
}
]
}
This endpoint retrieves all games (maps) for a specific match.
HTTP Request
GET https://api.balldontlie.io/dota/v1/match_maps
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| match_id | Yes | The ID of the match |
Player Match Map Stats
Get Player Match Map Stats
curl "https://api.balldontlie.io/dota/v1/player_match_map_stats?match_map_id=123" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/player_match_map_stats?match_map_id=123", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/player_match_map_stats",
headers={"Authorization": "YOUR_API_KEY"},
params={"match_map_id": 123}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 456,
"match_map_id": 123,
"player": {
"id": 2131,
"nickname": "N0tail"
},
"team": {
"id": 7,
"name": "OG"
},
"hero": {
"id": 106,
"name": "tiny",
"localized_name": "Tiny"
},
"kills": 8,
"deaths": 3,
"assists": 15,
"kda": 7.67,
"kp": 0.85,
"game_impact": 0.52,
"net_worth": 24500,
"gpm": 612,
"xpm": 720,
"damage": 28956,
"last_hits": 287,
"denies": 12,
"heal": 0
}
],
"meta": {
"next_cursor": 457,
"per_page": 25
}
}
This endpoint retrieves player statistics for a specific game (map).
HTTP Request
GET https://api.balldontlie.io/dota/v1/player_match_map_stats
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| match_map_id | null | Filter by match map (game) ID |
| player_id | null | Filter by player ID |
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Team Match Map Stats
Get Team Match Map Stats
curl "https://api.balldontlie.io/dota/v1/team_match_map_stats?match_map_id=123" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/team_match_map_stats?match_map_id=123", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/team_match_map_stats",
headers={"Authorization": "YOUR_API_KEY"},
params={"match_map_id": 123}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 789,
"match_map_id": 123,
"team": {
"id": 7,
"name": "OG"
},
"enemy_team": {
"id": 15,
"name": "Team Secret"
},
"side": "radiant",
"kills": 35,
"deaths": 18,
"assists": 78,
"net_worth": 85000,
"gpm": 2125,
"xpm": 2450,
"damage": 98765
}
],
"meta": {
"next_cursor": 790,
"per_page": 25
}
}
This endpoint retrieves team statistics for a specific game (map).
HTTP Request
GET https://api.balldontlie.io/dota/v1/team_match_map_stats
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| match_map_id | null | Filter by match map (game) ID |
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Player Overall Stats
Get Player Overall Stats
curl "https://api.balldontlie.io/dota/v1/player_overall_stats?per_page=25" \
-H "Authorization: YOUR_API_KEY"
fetch("https://api.balldontlie.io/dota/v1/player_overall_stats?per_page=25", {
headers: {
Authorization: "YOUR_API_KEY",
},
})
.then((response) => response.json())
.then((data) => console.log(data));
import requests
response = requests.get(
"https://api.balldontlie.io/dota/v1/player_overall_stats",
headers={"Authorization": "YOUR_API_KEY"},
params={"per_page": 25}
)
data = response.json()
The above command returns JSON structured like this:
{
"data": [
{
"id": 2160,
"player": {
"id": 2131,
"nickname": "N0tail"
},
"maps_played": 30,
"total_kills": 55,
"avg_kills": 1.833,
"total_deaths": 223,
"avg_deaths": 7.433,
"total_assists": 401,
"avg_assists": 13.367,
"kda": 2.045,
"kp": 0.6008,
"game_impact": 2.1068,
"avg_net_worth": 8568.533,
"avg_gpm": 286.367,
"avg_xpm": 416.133,
"avg_damage": 10609.5,
"avg_last_hits": 44.267,
"avg_denies": 2.233,
"avg_heal": 2979.067
},
{
"id": 288,
"player": {
"id": 287,
"nickname": "TOPSON"
},
"maps_played": 232,
"total_kills": 1704,
"avg_kills": 7.345,
"total_deaths": 1067,
"avg_deaths": 4.599,
"total_assists": 2798,
"avg_assists": 12.06,
"kda": 4.219,
"kp": 0.7319,
"game_impact": 27.3249,
"avg_net_worth": 22365.31,
"avg_gpm": 590.427,
"avg_xpm": 698.629,
"avg_damage": 32421.556,
"avg_last_hits": 317.345,
"avg_denies": 10.099,
"avg_heal": 594.724
}
],
"meta": {
"next_cursor": 289,
"per_page": 25
}
}
This endpoint retrieves overall career statistics for players.
HTTP Request
GET https://api.balldontlie.io/dota/v1/player_overall_stats
Query Parameters
| Parameter | Default | Description |
|---|---|---|
| player_ids | null | Filter by player IDs (array) |
| per_page | 25 | Number of results per page (max 100) |
| cursor | null | Cursor for pagination |
Error Codes
The BALLDONTLIE API uses the following error codes:
| Error Code | Meaning |
|---|---|
| 400 | Bad Request -- Your request is invalid |
| 401 | Unauthorized -- Your API key is wrong or you don't have access to this endpoint |
| 404 | Not Found -- The specified resource could not be found |
| 429 | Too Many Requests -- You're making too many requests! Slow down! |
| 500 | Internal Server Error -- We had a problem with our server. Try again later. |
Rate Limiting
Rate limits are enforced based on your account tier:
- FREE: 5 requests per minute
- ALL-STAR: 60 requests per minute
- GOAT: 600 requests per minute
When you exceed the rate limit, you'll receive a 429 status code. The response will include headers indicating your current rate limit status:
X-RateLimit-Limit: Maximum requests per minuteX-RateLimit-Remaining: Remaining requests in current windowX-RateLimit-Reset: Unix timestamp when the rate limit resets