HLTV data in Python, without the scraper.
There is no official HLTV API for Python. Python HLTV libraries download hltv.org pages, and scripted requests now get a Cloudflare 403 "Just a moment..." page instead. For code that keeps working, call a CS2 API with requests: results, HLTV-style ratings, the ranking and live scores are one GET each, with a free key.
- requests, httpx, pandas
- No scraping
- 500 free requests / month
{ "success": true, "data": [ { "id": "cs2-match-2399131", "eventName": "Stake Pulse Beat III", "bestOf": 3, "team1Name": "Ninjas in Pyjamas", "team2Name": "Betclic", "score": { "team1": 2, "team2": 1 } }, { "id": "cs2-match-2399130", "eventName": "Stake Pulse Beat III", "bestOf": 3, "team1Name": "Inner Circle", "team2Name": "Sangal", "score": { "team1": 2, "team2": 0 } }, "__more__" ]}Seamless Integration with our MCP Server
Give Claude, Cursor, and ChatGPT the CS2 data. One command writes the MCP config; agents call live scoreboard, team map stat, opening duel, and transfer tools instead of inventing match IDs, and your key stays on your machine.
MCPInstall the Cito MCP serverWhy do Python HLTV libraries stop working?
They are scrapers: they request hltv.org pages and parse the HTML. When we sent a plain Python request to hltv.org on 7 October 2026 it got 403 Forbidden with cf-mitigated: challenge, a Cloudflare check that needs a real browser. A library cannot parse a page it never receives.
The full error, and how to detect it in your own code: HLTV scraper alternative and the 403 fix.
| Python package | Latest release | How it gets data |
|---|---|---|
| hltv-async-api (PyPI) | 0.8.3, 3 June 2024 | Downloads hltv.org pages; its README shows 403 retries and proxy options |
| hltv-data (PyPI) | 0.4.0, 9 March 2024 | Downloads hltv.org pages with requests |
| SocksPls/hltv-api (GitHub) | No PyPI release | Downloads hltv.org pages; its issues report "Just a moment..." pages |
Step 1: How do I set up Python?
Install requests, create a free key at citoapi.com, and keep it in an environment variable. Every call sends it in the x-api-key header; nothing else is needed, no browser and no cookies.
pip install requests
export CITO_API_KEY="cito_..." # Windows PowerShell: $env:CITO_API_KEY = "cito_..."import os, requests
API = "https://api.citoapi.com/api/v1/cs2"
session = requests.Session()
session.headers["x-api-key"] = os.environ["CITO_API_KEY"]
def get(path: str, **params):
r = session.get(f"{API}{path}", params=params, timeout=10)
r.raise_for_status()
return r.json()Step 2: How do I get recent results?
GET /cs2/matches/results returns finished matches newest first with the event, the format and the series score. Filter one team with /cs2/teams/{slug}/results.
for m in get("/matches/results", limit=10)["data"]:
print(f"{m['eventName']}: {m['team1Name']} {m['team1Score']}-{m['team2Score']} {m['team2Name']} (Bo{m['bestOf']})")
vitality = get("/teams/vitality/results", limit=5)["data"]Step 3: How do I get a player's HLTV-style stats?
Players take the slug you already know. /cs2/players/{slug} has the career rating; /cs2/players/{slug}/matches has one row per map with rating, ADR, KAST, kills and deaths.
p = get("/players/zywoo")["data"]
print(p["nickname"], p["realName"], "career rating", p["rating"], "over", p["mapsPlayed"], "maps")
for row in get("/players/zywoo/matches", limit=5)["data"]:
print(row["map"]["mapName"], f"{row['kills']}-{row['deaths']}", "ADR", row["adr"], "rating", row["rating"])Step 4: How do I get the team ranking?
GET /cs2/rankings/world returns the world ranking with rank, points and weekly change; /cs2/rankings/vrs returns Valve's Regional Standings in the same shape.
ranking = get("/rankings/world", limit=10)
print("Week of", ranking["meta"]["rankingDate"])
for t in ranking["data"]:
print(t["rank"], t["teamName"], t["points"], "pts")Step 5: How do I follow live scores?
Poll GET /cs2/matches/live every 15 to 30 seconds. Each match carries the series score, the map in play, the round and the CT and T score on that map.
import time
while True:
for m in get("/matches/live")["data"]:
print(f"{m['team1Name']} {m['team1Score']}-{m['team2Score']} {m['team2Name']} | "
f"{m['currentMap']} round {m['currentRound']} (CT {m['currentScoreCt']} : T {m['currentScoreT']})")
time.sleep(30)Step 6: How do I load stats into pandas?
Every list endpoint returns data as an array of flat-ish objects, so pandas.json_normalize turns a response into a DataFrame in one line. The 90-day leaderboard is a good start for a model or a chart.
import pandas as pd
rows = get("/leaderboards/rating", limit=50)["data"]
df = pd.json_normalize(rows)[["playerName", "teamName", "maps", "rating", "adr", "kast", "kd"]]
print(df.sort_values("adr", ascending=False).head(10).to_string(index=False))Can I use httpx or asyncio instead?
Yes. The API is plain HTTPS and JSON, so any client works. With httpx you can fetch several players at once; stay under your plan's per-minute limit, which every response reports.
import asyncio, os, httpx
API = "https://api.citoapi.com/api/v1/cs2"
async def main():
async with httpx.AsyncClient(headers={"x-api-key": os.environ["CITO_API_KEY"]}, timeout=10) as c:
slugs = ["zywoo", "donk", "m0nesy"]
res = await asyncio.gather(*(c.get(f"{API}/players/{s}") for s in slugs))
for r in res:
p = r.raise_for_status().json()["data"]
print(p["nickname"], p["rating"])
asyncio.run(main())Endpoints you can ship with
Every endpoint the Python examples call.
Live matches
Poll the live list for every match in progress, open one match for its round, timer, bomb and players, or keep an SSE stream open and get each update pushed.
- GET
/api/v1/cs2/liveMatches in progress: series score, map in play, map score, current round.
- GET
/api/v1/cs2/live/{matchId}/stateOne live match: score, round, round timer, bomb status and players.
- GET
/api/v1/cs2/live/{matchId}/scoreboardSeries score, current map and round, and each player's kills, deaths, money and equipment.
- GET
/api/v1/cs2/live/{matchId}/roundsRound-by-round results so far: winner, side and how each round ended.
- GET
/api/v1/cs2/live/streamServer-Sent Events push of every live update. Every plan; counts like polling.
- GET
/api/v1/cs2/live/wsWebSocket with a room per match. Scale and Enterprise.
Results
Finished matches with the series score and winner, every map and its score, and the player lines behind them.
- GET
/api/v1/cs2/matches/recentFinished matches with final series score and winner, newest first.
- GET
/api/v1/cs2/matches/{matchId}One match: event, teams, series score, and maps.
- GET
/api/v1/cs2/matches/{matchId}/mapsEach map: name, score, winner, and the team that picked it.
- GET
/api/v1/cs2/matches/{matchId}/player-statsPer-player stats for each map: kills, deaths, ADR, KAST and rating.
- GET
/api/v1/cs2/teams/{teamIdOrSlug}/resultsOne team's finished matches, newest first.
- GET
/api/v1/cs2/events/{eventIdOrSlug}/resultsFinal placements and prize money of a tournament.
Schedule
What is on next: upcoming matches soonest first, today's slate, one team's fixtures, and the tournament calendar.
- GET
/api/v1/cs2/matches/upcomingScheduled matches, soonest first.
- GET
/api/v1/cs2/matches/todayMatches scheduled, live or finished today (UTC).
- GET
/api/v1/cs2/teams/{teamIdOrSlug}/upcomingOne team's scheduled matches.
- GET
/api/v1/cs2/events/upcomingScheduled tournaments, soonest first.
- GET
/api/v1/cs2/events/calendarTournaments in a date range, with from and to.
- GET
/api/v1/cs2/matches/{matchId}/streamsBroadcast links for a match, when known.
Player stats
Rating, ADR, KAST and K/D for every map a player has played, rolled up into career, yearly and recent-form views.
- GET
/api/v1/cs2/players/{playerIdOrSlug}Nickname, real name, photo, country, team, role and headline stats.
- GET
/api/v1/cs2/players/{playerIdOrSlug}/statsCareer totals plus a stat line for every map played.
- GET
/api/v1/cs2/players/{playerIdOrSlug}/seasonsA player's stats by year.
- GET
/api/v1/cs2/players/{playerIdOrSlug}/formThe most recent maps with kills, deaths, ADR, KAST and rating.
- GET
/api/v1/cs2/leaderboards/{metric}Top players by rating, ADR, KAST, K/D, kills or opening kills.
- GET
/api/v1/cs2/players/search?q={name}Find a player by nickname or real name.
Teams & rosters
Team profiles with the current roster, every past lineup, player moves between teams, and how each team plays each map.
- GET
/api/v1/cs2/teams/{teamIdOrSlug}Name, logo, country, region, world ranking and current roster.
- GET
/api/v1/cs2/teams/{teamIdOrSlug}/roster-historyEvery player who has played for the team, with start and end dates.
- GET
/api/v1/cs2/teams/{teamIdOrSlug}/map-statsPer-map win rate, rounds and pick/ban tendencies.
- GET
/api/v1/cs2/teams/{teamIdOrSlug}/vs/{opponentIdOrSlug}Record and past matches between two teams.
- GET
/api/v1/cs2/roster-changes/recentPlayers joining and leaving teams, newest first.
- GET
/api/v1/cs2/transfers/recentPlayer moves between teams, newest first.
Rankings
The world team ranking and the Valve Regional Standings, with points and movement, plus one table per region.
- GET
/api/v1/cs2/rankings/worldThe latest world team ranking.
- GET
/api/v1/cs2/rankings/vrsThe latest Valve Regional Standings (VRS).
- GET
/api/v1/cs2/rankings/region/{region}One region, with each team's world and regional rank.
- GET
/api/v1/cs2/rankingsOne row per team, with points and change since the previous ranking.
Skin prices
The current price of a skin on each marketplace, its daily price history, sell listings across markets, and the week's biggest movers.
- GET
/api/v1/cs2/skins?q={name}Search skins by name, weapon, rarity or price range.
- GET
/api/v1/cs2/skins/{skinIdOrSlug}/pricesThe current price on each marketplace, cheapest first, with offer counts.
- GET
/api/v1/cs2/skins/{skinIdOrSlug}/historyDaily cheapest price across marketplaces, years back on long-listed skins.
- GET
/api/v1/cs2/skins/{skinIdOrSlug}/order-bookSell listings per marketplace with counts, and the lowest ask.
- GET
/api/v1/cs2/skins/trendingBiggest gainers and losers over 1 day to 1 year, with sparklines.
- GET
/api/v1/cs2/skins/{skinIdOrSlug}One skin: weapon, rarity, collection, image and price summary.
Tournaments
Tournaments with dates, prize pool, tier and format, plus every match, the standings and the bracket.
- GET
/api/v1/cs2/events/liveTournaments being played now.
- GET
/api/v1/cs2/events/{eventIdOrSlug}Dates, location, prize pool, tier, teams and format.
- GET
/api/v1/cs2/events/{eventIdOrSlug}/matchesEvery match of the tournament.
- GET
/api/v1/cs2/events/{eventIdOrSlug}/standingsTeams with seed, final placement and prize.
- GET
/api/v1/cs2/events/{eventIdOrSlug}/bracketBracket, groups, play-in and Swiss, each slot with teams, scores and winner.
FAQs
HLTV and CS2 data in Python
Can't find what you're looking for? Contact our customer support team
Next steps
- HLTV APIIs there an HLTV API? No official one. What to use instead.
- HLTV API in JavaScriptHLTV-style CS2 data in Node.js and TypeScript.
- HLTV stats APIHLTV-style ratings, ADR, KAST and K/D as JSON.
- CS2 player stats APIRating, ADR, and KAST for every map played.
- CS2 results APISeries and map scores for every finished match.
- Free CS2 APIWhat a free key includes, with every limit stated.
- HLTV scraper alternativeWhy HLTV answers scrapers with a 403 challenge, and the fix.
- CS2 API docsEvery endpoint, its parameters, and a real response.
Build in Python:Â stat bots
Create a free key and run the examples above.