Skip to content

Esports API: Free Data Sources for Dota 2, CS2, League of Legends and Valorant

Free esports data sources, tested: OpenDota's endpoints answered with no key on 2 November 2026.

Last updated:

What Is an Esports API?

An esports API serves match results, teams, players, tournaments and live game state for competitive video games. This page covers that data side; for betting prices, see our esports odds API guide. The one source that serves full match data with no key and no account is OpenDota, a free, open-source Dota 2 API at https://api.opendota.com/api, run by volunteers rather than by Valve. Riot Games, Valve (the Steam Web API) and Faceit run official APIs, and each one gates most data behind a key tied to an account. Liquipedia's wiki API needs no key, but its terms ask for a named User-Agent.

We called every OpenDota endpoint on this page on 2 November 2026, at about 10:35 pm US Eastern. For the keyed APIs we read the docs and quote them, each fact marked "from the docs, checked November 2026". We did not sign up for keys, so no sample here calls them.

Base URLs and Access

HostGamesAccessOur call, 2 November 2026
https://api.opendota.com/apiDota 2No key; a paid key raises the limits200
https://api.steampowered.comSteam games, Dota 2 and CS2 includedKey for most methods200 on two methods, 403 on Dota 2 matches
https://liquipedia.net/{wiki}/api.phpOne wiki per esportNo key; a User-Agent with contact details200
https://na1.api.riotgames.com and other regionsLeague of Legends, TFT, LoR, ValorantRiot account and key401 with no key
https://open.faceit.com/data/v4Faceit matches and playersFaceit account and app key403 challenge page with no key
hltv.orgCS2No API403

What Each Source Covers

OpenDota covers Dota 2 only, in depth. /leagues listed 10,412 leagues, each tagged with a tier (2,485 "professional", 214 "premium"). /proPlayers listed 5,738 accounts, and Team Spirit's match history went back to May 2019.

CS2 has no equivalent. Steam serves the live player count and patch news without a key, Faceit's Data API needs one, Liquipedia holds results as wiki pages, and HLTV offers no API. For League of Legends and Valorant, Riot's API serves accounts, matches and ranked ladders; its API list at developer.riotgames.com/apis shows no endpoint for pro league schedules (checked November 2026).

ESPN's free feed carries no esports: sports/esports/lol/scoreboard returned 400, "sport 'esports' and/or league 'lol' are invalid". The ESPN API guide covers the leagues it does carry, and our free sports APIs page lists more free feeds. None of the sources here serves betting odds.

OpenDota Key Endpoints

Paths sit under https://api.opendota.com/api, and the OpenAPI spec at that root lists 55. Every one below returned 200 on 2 November 2026.

EndpointWhat it returnsExample
/proMatchesThe latest 100 pro matchesNewest: 9073699287, EPL Masters 2026
/matches/{match_id}One match: draft, players, gold graph250 KB for 9073699287
/leaguesEvery league with its tier10,412 leagues
/leagues/{league_id}/matchesMatches in one league19944: 483 matches since 20 July
/teams1,000 teams per page, by ratingTeam Spirit first, 1591.77
/teams/{team_id}One team's rating and record7119388: 974 wins, 651 losses
/teams/{team_id}/matchesA team's match history1,625 matches
/teams/{team_id}/playersEveryone who played for the team33 players, 5 current
/proPlayersPro accounts with team and name5,738 accounts
/players/{account_id}/recentMatchesA player's last 20 matcheskills, hero_id, gold_per_min
/heroes, /heroStatsHero ids and names; pro picks, wins and bans127 heroes
/liveTop games in progress100 games
/explorer?sql=Your SQL query on OpenDota's databaseSee the cURL sample

At the time of our call, 98 of the 100 games in /live had no league_id, and the 2 league games had empty team names.

OpenDota Match Endpoint

URL
https://api.opendota.com/api/matches/{match_id}

This endpoint returns everything OpenDota holds on one game. Our test match, 9073699287, was CyberHero against GamerLegion in EPL Masters 2026 on 2 November. GamerLegion, on the Dire side, won 32 kills to 12 in 38 minutes, and the response held 10 players and 24 draft entries.

FieldTypeWhat it holds
match_idintegerValve's match id, 9073699287
start_time, durationintegerUnix start time; length in seconds, 2288
radiant_winbooleanfalse: Dire won
radiant_score, dire_scoreintegerKills per side, 12 and 32
leagueobjectleagueid, name, tier
radiant_team, dire_teamobjectteam_id, name, tag, logo_url
series_idintegerGroups the games of one series, 1163541
picks_bansarrayThe draft: is_pick, hero_id, team, order
radiant_gold_advarrayRadiant's gold lead each minute, 39 values here
playersarrayname, hero_id, kills, deaths, assists, gold_per_min, net_worth
od_data.has_parsedbooleantrue when OpenDota parsed the replay

Parsed matches add teamfights, objectives and chat. Heroes come as numbers, so join hero_id to /heroes. In players, name holds the pro name (Yuma went 15/2/7 on Ursa) and personaname holds the Steam name.

Paging, Filters and SQL

ParameterWhat it didExample
less_than_match_idThe next 100 pro matches, older than that id/proMatches?less_than_match_id=9061220091
pageThe next 1,000 teams; pages start at 0/teams?page=1
limit, project3 rows, with the projected fields added/players/321580662/matches?limit=3&project=hero_id&project=kills
sqlRuns a URL-encoded query/explorer?sql=SELECT...

The player matches endpoint also filters on hero_id, lobby_type, date (days back) and win, per the spec. Skip /search?q= for pros: it matches Steam names, and our search for "Yatoro" returned 50 accounts in 2.8 seconds, none of them Team Spirit's player. Filter /proPlayers on name and team_id instead.

Code Examples

Each sample runs as written with no key. We ran all three on 2 November 2026.

Latest pro results in Python

Python
import requests

matches = requests.get('https://api.opendota.com/api/proMatches', timeout=10).json()
for m in matches[:5]:
    radiant = m['radiant_name'] or 'Radiant'
    dire = m['dire_name'] or 'Dire'
    winner = radiant if m['radiant_win'] else dire
    print(m['match_id'], m['league_name'].strip(), '|', radiant, 'vs', dire, '| won:', winner)

The first line it printed: "9073699287 EPL Masters 2026 | CyberHero vs GamerLegion | won: GamerLegion". Some rows have null team names, which is why the sample falls back to the side.

A team and a SQL query with cURL

cURL
# One team's rating and record (Team Spirit)
curl 'https://api.opendota.com/api/teams/7119388'

# Your own SQL: the last three games of league 19944
curl -G 'https://api.opendota.com/api/explorer' \
  --data-urlencode 'sql=SELECT match_id, radiant_win, duration FROM matches WHERE leagueid = 19944 ORDER BY match_id DESC LIMIT 3'

One match's player lines in JavaScript

JavaScript
// Node 18 or later
const base = 'https://api.opendota.com/api';
Promise.all([
  fetch(`${base}/heroes`).then(r => r.json()),
  fetch(`${base}/matches/9073699287`).then(r => r.json()),
]).then(([heroes, match]) => {
  const hero = Object.fromEntries(heroes.map(h => [h.id, h.localized_name]));
  console.log(match.league.name.trim(), '|', match.radiant_team.name, 'vs', match.dire_team.name);
  for (const p of match.players) {
    const side = p.isRadiant ? 'Radiant' : 'Dire';
    console.log(side, p.name || p.personaname, hero[p.hero_id], `${p.kills}/${p.deaths}/${p.assists}`);
  }
});

It printed the league and both teams, then ten player lines, from "Radiant sematary Windranger 2/8/5" to "Dire Bignum Hoodwink 6/3/14".

Riot, Steam and Faceit APIs

Riot Games API (from the docs, checked November 2026)

Signing in at developer.riotgames.com with a Riot account creates a development key, and those keys "deactivate every 24 hours". A personal key, meant for "just the developer or a small private community", allows "20 requests every 1 second" and "100 requests every 2 minutes" per region. Production keys start at 500 requests every 10 seconds. The docs give no separate number for the development key. Valorant is stricter: "personal keys are not offered for VALORANT", and Valorant apps need Riot Sign On, which only production keys get. The reference takes the key as a query or header parameter, and our call with neither returned 401.

Steam Web API

Valve's developer page says "All use of the Steam Web API requires the use of an API Key", issued at steamcommunity.com/dev/apikey after a Steam login. The partner docs say some methods "do not require authorization", and these two answered without a key:

EndpointWhat it returnsExample
/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=730Players in a game right nowCS2: player_count 399,360
/ISteamNews/GetNewsForApp/v2/?appid=570&count=3News and patch notesDota 2: "7.41f Gameplay Patch"

Dota 2 match history and CS2 player stats need the key. A key goes in key= or the x-webapi-key header (from the docs, checked November 2026).

Faceit Data API (from the docs, checked November 2026)

The Data API at https://open.faceit.com/data/v4 serves "all publicly available information on faceit.com": championships, hubs, matches with /matches/{match_id}/stats, players, rankings and teams, by game_id. You create an app in Faceit's developer portal, add a server-side or client-side key, and send it as a Bearer token. The reference lists a 429 "Too many requests" response but states no number.

Liquipedia API and HLTV

Liquipedia runs one MediaWiki wiki per game, each with an API at https://liquipedia.net/{wiki}/api.php. It needs no key, and its API terms say generic agents such as "Python-requests" are "likely to be blocked". We sent User-Agent: sportsapis.dev-guide/1.0 (https://sportsapis.dev; info@sportsapis.dev) and Accept-Encoding: gzip, spaced 3 seconds apart.

EndpointWhat it returnsExample
dota2/api.php?action=query&list=search&srsearch=Team%20Spirit&srlimit=5&format=jsonFull-text search1,974 hits, "Team Spirit" first
counterstrike/api.php?action=opensearch&search=Team%20Vitality&limit=5&format=jsonTitle matches with URLs4 pages
dota2/api.php?action=parse&page=Team_Spirit&prop=wikitext&format=jsonA page's source53,492 characters

Wikitext is raw markup, so you parse templates such as {{Infobox team}} yourself. Its teamid=7119388 is OpenDota's id for Team Spirit, a ready join between the two.

HLTV.org, a site with "CS2 news, CS2 match information", publishes no API. Its terms, effective 27 January 2025, forbid users to "conduct, facilitate or organize data mining or web scraping in relation to the Website". hltv.org answered our script and our fetch tool with 403, so we read the Internet Archive copy of 3 October 2026. This page documents no HLTV endpoint.

Errors You Will See

OpenDota 404 and 400

/matches/1, /matches/abc and an unknown path returned 404 with {"error":"Not Found"}. /players/abc returned 400, "invalid account id". /explorer with no sql returned 400, "sql is not a string", and a bad column returned 400 with the database error.

OpenDota 200 with an empty body

/teams/999999999 and /leagues/99999999 returned 200 and zero bytes, so response.json() throws. Check the length before you parse.

Keyed and blocked calls

Steam's IDOTA2Match_570/GetMatchHistory returned 403, "Please verify your key= parameter", and ISteamApps/GetAppList/v2/ returned 404, "Method 'GetAppList' not found". Riot returned 401, "apikey or authorization header is empty". Faceit's API answered a no-key call with a 403 challenge page. Liquipedia reports errors inside a 200: a search with no srsearch returned "code":"missingparam".

Limitations and Terms

OpenDota runs on a best-effort basis

OpenDota's FAQ says: "The service is provided on a best-effort basis. Since it relies on Valve infrastucture, we cannot make any guarantees of availability." Its code is MIT-licensed, and the data comes from the Steam Web API and match replays. We found no separate data licence.

  • OpenDota limits: no key gives 60 calls a minute and 3,000 a day (/api/metadata). Each data response we got carried x-rate-limit-remaining-minute and x-rate-limit-remaining-day. A key from opendota.com/api-keys costs $0.01 per 100 calls, needs a payment method and allows 3,000 calls a minute. We hit no 429.
  • Riot: the General Policies say "Your product cannot feature betting or gambling functionality."
  • Steam: the API terms license Steam data to end users "for their personal use" and cap you at 100,000 calls a day.
  • Liquipedia: 1 request per 2 seconds, action=parse once per 30 seconds, gzip, CC-BY-SA 3.0 with credit to Liquipedia, and no "automated access to non-API endpoints". Breaking the rules brings "automated temporary IP bans".

For an odds product, a paid app or anything with an SLA, use licensed esports data providers: compare licensed sports data providers and the esports odds APIs.

Browse API Providers →

Community Resources

Frequently Asked Questions

Yes. OpenDota serves Dota 2 pro matches, teams, players and live games at https://api.opendota.com/api with no key and no account, up to 60 calls a minute and 3,000 a day. Liquipedia's wiki APIs need no key either, only a named User-Agent and one request every 2 seconds. Riot, Steam and Faceit need a key tied to an account.
No. OpenDota is a volunteer-run, open-source project, and Valve does not run it. It collects data from the Steam Web API and from match replays. Valve's own Steam Web API serves Dota 2 match history, but only with a key, and our keyless call returned 403.
For OpenDota, no: a paid key only raises the limits. Liquipedia needs no key either. Riot needs one from a Riot account, and development keys deactivate every 24 hours. Steam needs one for most methods, though the live player count and news answered without it. Faceit needs an app key from its developer portal.
Dota 2 has the deepest free coverage, through OpenDota. For CS2, Steam serves player counts and news, Faceit serves its own matches with a key, and Liquipedia holds results as wiki pages. League of Legends and Valorant player and match data comes from Riot's keyed API. ESPN's free API has no esports at all.
Without a key: 60 calls a minute and 3,000 a day, per https://api.opendota.com/api/metadata. Each data call reports what is left in x-rate-limit-remaining-minute and x-rate-limit-remaining-day. A paid key allows 3,000 calls a minute at $0.01 per 100 calls, and OpenDota does not bill 404, 429 or 500 responses.
Call https://api.opendota.com/api/proMatches for the latest 100 pro matches with teams, league and radiant_win. Pass a match_id to /matches/{match_id} for the draft and player stats, and add less_than_match_id to /proMatches to page back in time.
Check each source. Riot's policies say a product "cannot feature betting or gambling functionality", Steam licenses data for end users' personal use, and HLTV forbids scraping. OpenDota promises only best effort. For production, compare licensed data providers or browse the API directory.

Need Licensed Esports Data?

OpenDota and the wiki APIs suit hobby projects and research. A betting or paid product needs licensed data, support and an uptime promise. Compare providers in our directory.