NAV
shell javascript python

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:

  1. Copy this URL: https://www.balldontlie.io/openapi.yml
  2. Share it with your preferred AI assistant (ChatGPT, Claude, Gemini, etc.)
  3. Tell the AI what you want to build (e.g., "Create a dashboard showing OG's match results")
  4. The AI will read the OpenAPI spec and write the code for you

Example prompts to try:

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_KEY with 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:

When you exceed the rate limit, you'll receive a 429 status code. The response will include headers indicating your current rate limit status: