An HLTV API alternative, in four swaps.
HLTV publishes no official API, so most HLTV-style code fetches web pages and breaks when they change. Almost all of it does four jobs: recent results, a player's ratings, the team ranking and live scores. Each has one documented CS2 API endpoint; this guide swaps them one at a time.
- 4 endpoint swaps
- Python and Node.js
- Not affiliated with HLTV
{ "success": true, "meta": { "ranking": "world", "rankingDate": "2026-10-03" }, "data": [ { "rank": 1, "teamName": "Spirit", "points": 1000 }, { "rank": 2, "teamName": "Vitality", "points": 676 }, { "rank": 3, "teamName": "FURIA", "points": 524 } ... ]}Which endpoint replaces each job?
Results, player stats, the ranking and live scores each map to a single endpoint, and the IDs are stable, so you can store them. Teams and players also take a readable slug, which spares you a lookup step.
| Job | CS2 API endpoint | Notes |
|---|---|---|
| Recent results | GET /cs2/matches/results | Series score, event, format, maps; page with page and limit. |
| A player's ratings | GET /cs2/players/{slug}/matches | One row per map: kills, deaths, ADR, KAST, rating. |
| Team ranking | GET /cs2/teams/rankings or /cs2/rankings/vrs | World ranking or Valve Regional Standings, with change. |
| Live scores | GET /cs2/matches/live | Series score, map in play, map score, round. |
Swap 1: How do I get recent results?
One call returns finished matches newest first with the final series score. Filter by team with /cs2/teams/{slug}/results instead of walking a results page.
import os, requests
API = "https://api.citoapi.com/api/v1/cs2"
H = {"x-api-key": os.environ["CITO_API_KEY"]}
for m in requests.get(f"{API}/matches/results", headers=H, params={"limit": 10}).json()["data"]:
print(m["eventName"], "|", m["team1Name"], m["team1Score"], "-", m["team2Score"], m["team2Name"])Swap 2: How do I get a player's rating and ADR?
GET /cs2/players/{slug}/matches returns one line per map with rating, ADR and KAST, so recent form is an average over the rows you choose. Career numbers are on /cs2/players/{slug}/career.
rows = requests.get(f"{API}/players/zywoo/matches", headers=H, params={"limit": 20}).json()["data"]
rated = [r for r in rows if r["rating"] is not None]
print("maps:", len(rated))
print("rating:", round(sum(r["rating"] for r in rated) / len(rated), 2))
print("ADR:", round(sum(r["adr"] for r in rated if r["adr"] is not None) / len(rated), 1))Swap 3: How do I get the team ranking?
GET /cs2/teams/rankings returns the world ranking with points and change since the previous table; GET /cs2/rankings/vrs returns the Valve Regional Standings. meta.rankingDate says which week you have.
const res = await fetch("https://api.citoapi.com/api/v1/cs2/rankings/vrs?limit=10", {
headers: { "x-api-key": process.env.CITO_API_KEY },
})
const { data, meta } = await res.json()
console.log("VRS", meta.rankingDate)
for (const t of data) console.log(t.rank, t.teamName, t.points, t.change)| Rank | Team | Points | Change | Region |
|---|---|---|---|---|
| 1 | Spirit | 2,048 | – | Europe |
| 2 | Vitality | 1,925 | ▲1 | Europe |
| 3 | Legacy | 1,894 | ▼1 | South America |
| 4 | MOUZ | 1,874 | – | Europe |
| 5 | Falcons | 1,863 | – | Europe |
| 6 | FURIA | 1,834 | ▲1 | South America |
| 7 | G2 | 1,811 | ▼1 | Europe |
| 8 | FUT | 1,796 | – | Europe |
| 9 | Aurora | 1,742 | – | Europe |
| 10 | Astralis | 1,619 | ▲5 | Europe |
Swap 4: How do I get live scores?
Poll GET /cs2/matches/live, or keep the SSE stream at /cs2/live/stream open and have updates pushed. Each match carries the series score, the map in play, its score and the current round.
const res = await fetch("https://api.citoapi.com/api/v1/cs2/matches/live", { headers: { "x-api-key": process.env.CITO_API_KEY } })
for (const m of (await res.json()).data) {
console.log(`${m.team1Name} ${m.team1Score}-${m.team2Score} ${m.team2Name} | ${m.currentMap} ${m.currentMapScore?.team1}-${m.currentMapScore?.team2} R${m.currentRound}`)
}What changes for my code after the swap?
Requests return JSON in one documented shape instead of HTML you parse, so a website redesign no longer breaks your app. Rate limits are published per plan, and a 429 says exactly how long to wait in Retry-After instead of an IP block.
Frequently asked questions
Something missing? Email support@citoapi.com.
Does HLTV have an official API?
No. HLTV does not publish an official public API, so libraries that call themselves an HLTV API read its web pages.
What is the best HLTV API alternative for Python?
A documented CS2 API called with requests. The CS2 API covers results, per-map player ratings, the world ranking and VRS, and live scores, with a free key.
Can I get HLTV-style player ratings from an API?
Yes. GET /cs2/players/{slug}/matches returns rating, ADR and KAST for each map, and /career has the career totals.
Is the CS2 API affiliated with HLTV?
No. CS2 API is operated by Cito API and is not affiliated with HLTV.
Related
- HLTV API alternativeNo official HLTV API exists. What to use instead.
- CS2 player stats APIRating, ADR, KAST and K/D per map, career and form.
- CS2 rankings APIWorld ranking and Valve Regional Standings with movement.
- CS2 live score APISeries, map and round score for every match in progress.
- CS2 API in PythonLive scores, results and player stats with requests.
- CS2 API comparisonEvery CS2 data provider side by side: access, pricing, delivery and coverage.
Build CS2 live score apps
Free is for building and testing. Paid plans add commercial use, real-time live data and the full archive.