cs2-api-client 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cito API
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: cs2-api-client
3
+ Version: 1.0.0
4
+ Summary: CS2 API client: Counter-Strike 2 live scores, match results, player stats, teams, rankings (world + VRS), events and skin prices. Standard library only.
5
+ Author: Cito API
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://cs2-api.org
8
+ Project-URL: Documentation, https://cs2-api.org/endpoints/
9
+ Project-URL: Source, https://github.com/achillesscriptsvip/cs2-api
10
+ Project-URL: Issues, https://github.com/achillesscriptsvip/cs2-api/issues
11
+ Project-URL: Get a free API key, https://citoapi.com/signup?game=cs2&utm_source=pypi&utm_medium=referral&utm_campaign=cs2-api-package
12
+ Keywords: cs2,cs2-api,counter-strike,counter-strike-2,csgo,hltv,hltv-api,esports,skins,skin-prices,api-client
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Games/Entertainment
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # cs2-api: Counter-Strike 2 API client for JavaScript and Python
25
+
26
+ [![npm](https://img.shields.io/npm/v/cs2-api)](https://www.npmjs.com/package/cs2-api) [![PyPI](https://img.shields.io/pypi/v/cs2-api-client)](https://pypi.org/project/cs2-api-client/) [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
27
+
28
+ This is the official client for the **CS2 API** at [cs2-api.org](https://cs2-api.org). It covers:
29
+
30
+ - live Counter-Strike 2 match scores and round-level scoreboards
31
+ - match results, schedules and streams
32
+ - player stats (rating, ADR, KAST, K/D) and leaderboards
33
+ - teams, rosters and head-to-head
34
+ - the world ranking and Valve Regional Standings (VRS)
35
+ - events, standings and brackets
36
+ - transfers
37
+ - **CS2 skin prices across 25+ marketplaces**, with price history
38
+
39
+ It has zero dependencies. The JavaScript client ships TypeScript types and works as ESM or CommonJS; the Python client needs only the standard library.
40
+
41
+ > **You need a free API key** (500 requests/month, no card): **[Get your key](https://citoapi.com/signup?game=cs2&utm_source=npm-pypi&utm_medium=referral&utm_campaign=cs2-api-package)**
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ npm install cs2-api
47
+ ```
48
+
49
+ ```bash
50
+ pip install cs2-api-client
51
+ ```
52
+
53
+ ## JavaScript / TypeScript
54
+
55
+ ```js
56
+ import { CS2Api } from "cs2-api"; // or: const { CS2Api } = require("cs2-api")
57
+
58
+ const cs2 = new CS2Api(); // reads process.env.CITO_API_KEY
59
+
60
+ const live = await cs2.live(); // every match being played right now
61
+ for (const m of live) {
62
+ console.log(`${m.team1Name} ${m.team1Score}-${m.team2Score} ${m.team2Name} · ${m.currentMap}`);
63
+ }
64
+
65
+ const ranking = await cs2.worldRanking({ limit: 10 });
66
+ const vitality = await cs2.team("vitality"); // roster + recent results
67
+ const donk = await cs2.player("donk");
68
+ const prices = await cs2.skinPrices("ak-47-redline-field-tested-1ab1a2dc");
69
+ ```
70
+
71
+ ## Python
72
+
73
+ ```python
74
+ from cs2_api import CS2Api
75
+
76
+ cs2 = CS2Api() # reads CITO_API_KEY
77
+
78
+ for m in cs2.live():
79
+ print(f"{m['team1Name']} {m['team1Score']}-{m['team2Score']} {m['team2Name']} · {m['currentMap']}")
80
+
81
+ ranking = cs2.world_ranking(limit=10)
82
+ vitality = cs2.team("vitality")
83
+ donk = cs2.player("donk")
84
+ prices = cs2.skin_prices("ak-47-redline-field-tested-1ab1a2dc")
85
+ ```
86
+
87
+ ## Methods
88
+
89
+ JavaScript uses camelCase and Python uses snake_case (`worldRanking` / `world_ranking`). Teams, players and events accept an id or a slug (`vitality`, `donk`).
90
+
91
+ | Area | Methods |
92
+ |---|---|
93
+ | Live | `live()`, `liveScoreboard(matchId)`: sides, round, player K/D/ADR |
94
+ | Matches | `upcoming()`, `results()`, `today()`, `match(id)`, `matchMaps(id)`, `matchPlayerStats(id)`, `matchStreams(id)` |
95
+ | Teams | `team(slug)`, `teamMatches(slug)`, `teamStats(slug)`, `teamMapStats(slug)`, `headToHead(a, b)` |
96
+ | Players | `player(slug)`, `searchPlayers(q)`, `playerStats(slug)`, `playerMatches(slug)` |
97
+ | Events | `events({ status })`, `event(slug)`, `eventMatches(slug)`, `eventStandings(slug)`, `eventBracket(slug)` |
98
+ | Rankings & stats | `worldRanking()`, `vrsRanking()`, `leaderboard(metric)`, `transfers()`, `search(q)` |
99
+ | Skins | `searchSkins(q)`, `skin(slug)`, `skinPrices(slug)`, `skinHistory(slug)`, `trendingSkins()` |
100
+ | Anything else | `get(path, query, { raw })`: any of the 140+ endpoints, with `meta` for pagination when `raw: true` |
101
+
102
+ Errors throw `CS2ApiError` (JS) or raise `CS2ApiError` (Python), with `status` and the API's message.
103
+
104
+ ## Use cases
105
+
106
+ - **Discord bots**: [cs2-discord-bot](https://github.com/achillesscriptsvip/cs2-discord-bot)
107
+ - **Stream overlays**: [cs2-obs-overlay](https://github.com/achillesscriptsvip/cs2-obs-overlay)
108
+ - **Skin trading tools**: [cs2-skin-price-api](https://github.com/achillesscriptsvip/cs2-skin-price-api)
109
+ - **Moving off HLTV scrapers**: [hltv-api-alternative](https://github.com/achillesscriptsvip/hltv-api-alternative)
110
+ - Fantasy, analytics dashboards, prediction models and AI agents
111
+
112
+ ## Keep your key safe
113
+
114
+ Keep it in an environment variable or `.env` (git-ignored). Call the API from your server, never from browser code. If a key leaks, revoke it in your dashboard and create a new one.
115
+
116
+ ## Links
117
+
118
+ - CS2 API docs and endpoints: https://cs2-api.org/endpoints/
119
+ - Pricing: https://citoapi.com/pricing
120
+ - Issues: https://github.com/achillesscriptsvip/cs2-api/issues
121
+
122
+ Not affiliated with Valve, HLTV, ESL, BLAST or PGL. MIT licensed.
@@ -0,0 +1,99 @@
1
+ # cs2-api: Counter-Strike 2 API client for JavaScript and Python
2
+
3
+ [![npm](https://img.shields.io/npm/v/cs2-api)](https://www.npmjs.com/package/cs2-api) [![PyPI](https://img.shields.io/pypi/v/cs2-api-client)](https://pypi.org/project/cs2-api-client/) [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
4
+
5
+ This is the official client for the **CS2 API** at [cs2-api.org](https://cs2-api.org). It covers:
6
+
7
+ - live Counter-Strike 2 match scores and round-level scoreboards
8
+ - match results, schedules and streams
9
+ - player stats (rating, ADR, KAST, K/D) and leaderboards
10
+ - teams, rosters and head-to-head
11
+ - the world ranking and Valve Regional Standings (VRS)
12
+ - events, standings and brackets
13
+ - transfers
14
+ - **CS2 skin prices across 25+ marketplaces**, with price history
15
+
16
+ It has zero dependencies. The JavaScript client ships TypeScript types and works as ESM or CommonJS; the Python client needs only the standard library.
17
+
18
+ > **You need a free API key** (500 requests/month, no card): **[Get your key](https://citoapi.com/signup?game=cs2&utm_source=npm-pypi&utm_medium=referral&utm_campaign=cs2-api-package)**
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install cs2-api
24
+ ```
25
+
26
+ ```bash
27
+ pip install cs2-api-client
28
+ ```
29
+
30
+ ## JavaScript / TypeScript
31
+
32
+ ```js
33
+ import { CS2Api } from "cs2-api"; // or: const { CS2Api } = require("cs2-api")
34
+
35
+ const cs2 = new CS2Api(); // reads process.env.CITO_API_KEY
36
+
37
+ const live = await cs2.live(); // every match being played right now
38
+ for (const m of live) {
39
+ console.log(`${m.team1Name} ${m.team1Score}-${m.team2Score} ${m.team2Name} · ${m.currentMap}`);
40
+ }
41
+
42
+ const ranking = await cs2.worldRanking({ limit: 10 });
43
+ const vitality = await cs2.team("vitality"); // roster + recent results
44
+ const donk = await cs2.player("donk");
45
+ const prices = await cs2.skinPrices("ak-47-redline-field-tested-1ab1a2dc");
46
+ ```
47
+
48
+ ## Python
49
+
50
+ ```python
51
+ from cs2_api import CS2Api
52
+
53
+ cs2 = CS2Api() # reads CITO_API_KEY
54
+
55
+ for m in cs2.live():
56
+ print(f"{m['team1Name']} {m['team1Score']}-{m['team2Score']} {m['team2Name']} · {m['currentMap']}")
57
+
58
+ ranking = cs2.world_ranking(limit=10)
59
+ vitality = cs2.team("vitality")
60
+ donk = cs2.player("donk")
61
+ prices = cs2.skin_prices("ak-47-redline-field-tested-1ab1a2dc")
62
+ ```
63
+
64
+ ## Methods
65
+
66
+ JavaScript uses camelCase and Python uses snake_case (`worldRanking` / `world_ranking`). Teams, players and events accept an id or a slug (`vitality`, `donk`).
67
+
68
+ | Area | Methods |
69
+ |---|---|
70
+ | Live | `live()`, `liveScoreboard(matchId)`: sides, round, player K/D/ADR |
71
+ | Matches | `upcoming()`, `results()`, `today()`, `match(id)`, `matchMaps(id)`, `matchPlayerStats(id)`, `matchStreams(id)` |
72
+ | Teams | `team(slug)`, `teamMatches(slug)`, `teamStats(slug)`, `teamMapStats(slug)`, `headToHead(a, b)` |
73
+ | Players | `player(slug)`, `searchPlayers(q)`, `playerStats(slug)`, `playerMatches(slug)` |
74
+ | Events | `events({ status })`, `event(slug)`, `eventMatches(slug)`, `eventStandings(slug)`, `eventBracket(slug)` |
75
+ | Rankings & stats | `worldRanking()`, `vrsRanking()`, `leaderboard(metric)`, `transfers()`, `search(q)` |
76
+ | Skins | `searchSkins(q)`, `skin(slug)`, `skinPrices(slug)`, `skinHistory(slug)`, `trendingSkins()` |
77
+ | Anything else | `get(path, query, { raw })`: any of the 140+ endpoints, with `meta` for pagination when `raw: true` |
78
+
79
+ Errors throw `CS2ApiError` (JS) or raise `CS2ApiError` (Python), with `status` and the API's message.
80
+
81
+ ## Use cases
82
+
83
+ - **Discord bots**: [cs2-discord-bot](https://github.com/achillesscriptsvip/cs2-discord-bot)
84
+ - **Stream overlays**: [cs2-obs-overlay](https://github.com/achillesscriptsvip/cs2-obs-overlay)
85
+ - **Skin trading tools**: [cs2-skin-price-api](https://github.com/achillesscriptsvip/cs2-skin-price-api)
86
+ - **Moving off HLTV scrapers**: [hltv-api-alternative](https://github.com/achillesscriptsvip/hltv-api-alternative)
87
+ - Fantasy, analytics dashboards, prediction models and AI agents
88
+
89
+ ## Keep your key safe
90
+
91
+ Keep it in an environment variable or `.env` (git-ignored). Call the API from your server, never from browser code. If a key leaks, revoke it in your dashboard and create a new one.
92
+
93
+ ## Links
94
+
95
+ - CS2 API docs and endpoints: https://cs2-api.org/endpoints/
96
+ - Pricing: https://citoapi.com/pricing
97
+ - Issues: https://github.com/achillesscriptsvip/cs2-api/issues
98
+
99
+ Not affiliated with Valve, HLTV, ESL, BLAST or PGL. MIT licensed.
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cs2-api-client"
7
+ version = "1.0.0"
8
+ description = "CS2 API client: Counter-Strike 2 live scores, match results, player stats, teams, rankings (world + VRS), events and skin prices. Standard library only."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Cito API" }]
14
+ keywords = ["cs2", "cs2-api", "counter-strike", "counter-strike-2", "csgo", "hltv", "hltv-api", "esports", "skins", "skin-prices", "api-client"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Operating System :: OS Independent",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Games/Entertainment",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ "Typing :: Typed",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://cs2-api.org"
26
+ Documentation = "https://cs2-api.org/endpoints/"
27
+ Source = "https://github.com/achillesscriptsvip/cs2-api"
28
+ Issues = "https://github.com/achillesscriptsvip/cs2-api/issues"
29
+ "Get a free API key" = "https://citoapi.com/signup?game=cs2&utm_source=pypi&utm_medium=referral&utm_campaign=cs2-api-package"
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,182 @@
1
+ """cs2-api: Counter-Strike 2 esports and skin market API client (Python 3.8+, standard library only).
2
+
3
+ Live scores, match results, schedules, player stats (rating, ADR, KAST), teams and rosters, the
4
+ world ranking and Valve Regional Standings, events and brackets, transfers, and skin prices across
5
+ marketplaces.
6
+
7
+ Get a free API key (500 requests/month): https://cs2-api.org
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ import urllib.error
15
+ import urllib.parse
16
+ import urllib.request
17
+ from typing import Any, Dict, Optional
18
+
19
+ __version__ = "1.0.0"
20
+ __all__ = ["CS2Api", "CS2ApiError"]
21
+
22
+ DEFAULT_BASE_URL = "https://api.citoapi.com/api/v1"
23
+
24
+
25
+ class CS2ApiError(Exception):
26
+ def __init__(self, message: str, status: int = 0, body: Any = None):
27
+ super().__init__(message)
28
+ self.status = status
29
+ self.body = body
30
+ self.code = ((body or {}).get("error") or {}).get("code") if isinstance(body, dict) else None
31
+
32
+
33
+ def _q(value: str) -> str:
34
+ return urllib.parse.quote(str(value), safe="")
35
+
36
+
37
+ class CS2Api:
38
+ def __init__(self, api_key: Optional[str] = None, base_url: str = DEFAULT_BASE_URL, timeout: float = 20):
39
+ self.api_key = api_key if api_key is not None else os.environ.get("CITO_API_KEY")
40
+ self.base_url = base_url.rstrip("/")
41
+ self.timeout = timeout
42
+ if not self.api_key:
43
+ raise CS2ApiError("Missing API key. Get a free key at https://cs2-api.org and set CITO_API_KEY (or pass api_key=).")
44
+
45
+ def get(self, path: str, query: Optional[Dict[str, Any]] = None, raw: bool = False) -> Any:
46
+ """Any endpoint. Returns `data`, or the whole response (data + meta) with raw=True."""
47
+ params = {k: v for k, v in (query or {}).items() if v is not None and v != ""}
48
+ url = self.base_url + ("" if path.startswith("/") else "/") + path
49
+ if params:
50
+ url += "?" + urllib.parse.urlencode(params)
51
+ req = urllib.request.Request(url, headers={"x-api-key": self.api_key, "accept": "application/json", "user-agent": f"cs2-api-python/{__version__}"})
52
+ try:
53
+ with urllib.request.urlopen(req, timeout=self.timeout) as res:
54
+ body = json.loads(res.read().decode("utf-8"))
55
+ except urllib.error.HTTPError as err:
56
+ try:
57
+ body = json.loads(err.read().decode("utf-8"))
58
+ except Exception:
59
+ body = None
60
+ message = ((body or {}).get("error") or {}).get("message") if isinstance(body, dict) else None
61
+ raise CS2ApiError(message or f"Request failed with status {err.code}", err.code, body) from None
62
+ if isinstance(body, dict) and body.get("success") is False:
63
+ raise CS2ApiError(((body.get("error") or {}).get("message")) or "Request failed", 200, body)
64
+ return body if raw else (body.get("data") if isinstance(body, dict) else body)
65
+
66
+ # ---- Live ----
67
+ def live(self):
68
+ """Every match being played now, with series score, current map and map score."""
69
+ return self.get("/cs2/live")
70
+
71
+ def live_scoreboard(self, match_id: str):
72
+ """Round-level scoreboard for one live match: sides, round, player K/D/ADR, money."""
73
+ return self.get(f"/cs2/live/{_q(match_id)}/scoreboard")
74
+
75
+ # ---- Matches ----
76
+ def upcoming(self, limit: int = 50):
77
+ return self.get("/cs2/matches/upcoming", {"limit": limit})
78
+
79
+ def results(self, limit: int = 50):
80
+ return self.get("/cs2/matches/results", {"limit": limit})
81
+
82
+ def today(self):
83
+ return self.get("/cs2/matches/today")
84
+
85
+ def match(self, match_id: str):
86
+ return self.get(f"/cs2/matches/{_q(match_id)}")
87
+
88
+ def match_maps(self, match_id: str):
89
+ return self.get(f"/cs2/matches/{_q(match_id)}/maps")
90
+
91
+ def match_player_stats(self, match_id: str):
92
+ return self.get(f"/cs2/matches/{_q(match_id)}/player-stats")
93
+
94
+ def match_streams(self, match_id: str):
95
+ return self.get(f"/cs2/matches/{_q(match_id)}/streams")
96
+
97
+ # ---- Teams ----
98
+ def team(self, team: str):
99
+ """Team with roster and recent results. Accepts an id or a slug such as "vitality"."""
100
+ return self.get(f"/cs2/teams/{_q(team)}")
101
+
102
+ def team_matches(self, team: str, limit: int = 20):
103
+ return self.get(f"/cs2/teams/{_q(team)}/matches", {"limit": limit})
104
+
105
+ def team_stats(self, team: str):
106
+ return self.get(f"/cs2/teams/{_q(team)}/stats")
107
+
108
+ def team_map_stats(self, team: str):
109
+ return self.get(f"/cs2/teams/{_q(team)}/map-stats")
110
+
111
+ def head_to_head(self, team_a: str, team_b: str):
112
+ return self.get(f"/cs2/teams/{_q(team_a)}/h2h/{_q(team_b)}")
113
+
114
+ # ---- Players ----
115
+ def player(self, player: str):
116
+ """Player profile. Accepts an id or a slug such as "donk"."""
117
+ return self.get(f"/cs2/players/{_q(player)}")
118
+
119
+ def search_players(self, query: str, limit: int = 10):
120
+ return self.get("/cs2/players/search", {"q": query, "limit": limit})
121
+
122
+ def player_stats(self, player: str):
123
+ return self.get(f"/cs2/players/{_q(player)}/stats")
124
+
125
+ def player_matches(self, player: str, limit: int = 20):
126
+ return self.get(f"/cs2/players/{_q(player)}/matches", {"limit": limit})
127
+
128
+ # ---- Events ----
129
+ def events(self, status: str = "upcoming", limit: int = 50):
130
+ """status: "upcoming", "live" or "recent"."""
131
+ path = {"live": "/cs2/events/live", "recent": "/cs2/events/recent"}.get(status, "/cs2/events/upcoming")
132
+ return self.get(path, {"limit": limit})
133
+
134
+ def event(self, event: str):
135
+ return self.get(f"/cs2/events/{_q(event)}")
136
+
137
+ def event_matches(self, event: str):
138
+ return self.get(f"/cs2/events/{_q(event)}/matches")
139
+
140
+ def event_standings(self, event: str):
141
+ return self.get(f"/cs2/events/{_q(event)}/standings")
142
+
143
+ def event_bracket(self, event: str):
144
+ return self.get(f"/cs2/events/{_q(event)}/bracket")
145
+
146
+ # ---- Rankings and stats ----
147
+ def world_ranking(self, limit: int = 30):
148
+ return self.get("/cs2/rankings/world", {"limit": limit})
149
+
150
+ def vrs_ranking(self, limit: int = 30):
151
+ """Valve Regional Standings."""
152
+ return self.get("/cs2/rankings/vrs", {"limit": limit})
153
+
154
+ def leaderboard(self, metric: str, limit: int = 20):
155
+ """metric: e.g. "rating", "adr", "kast", "kd", "kills", "opening-kills"."""
156
+ return self.get(f"/cs2/leaderboards/{_q(metric)}", {"limit": limit})
157
+
158
+ def transfers(self, limit: int = 50):
159
+ return self.get("/cs2/transfers/recent", {"limit": limit})
160
+
161
+ def search(self, query: str):
162
+ """Teams, players, events and matches matching a name."""
163
+ return self.get("/cs2/search", {"q": query})
164
+
165
+ # ---- Skins ----
166
+ def search_skins(self, query: Optional[str] = None, **filters: Any):
167
+ """Search one word at a time ("redline"), or pass weapon=..., skin_name=... for an exact skin."""
168
+ return self.get("/cs2/skins/search", {"q": query, **filters})
169
+
170
+ def skin(self, slug: str):
171
+ return self.get(f"/cs2/skins/{_q(slug)}")
172
+
173
+ def skin_prices(self, slug: str):
174
+ """The skin's price on every marketplace, with listing counts and buy links."""
175
+ return self.get(f"/cs2/skins/{_q(slug)}/prices")
176
+
177
+ def skin_history(self, slug: str, range: Optional[str] = None):
178
+ return self.get(f"/cs2/skins/{_q(slug)}/history", {"range": range})
179
+
180
+ def trending_skins(self, timeframe: str = "7d", limit: int = 20, min_price: Optional[float] = None):
181
+ """Biggest movers: {"gainers": [...], "losers": [...]}."""
182
+ return self.get("/cs2/skins/trending", {"timeframe": timeframe, "limit": limit, "minPrice": min_price})
File without changes
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: cs2-api-client
3
+ Version: 1.0.0
4
+ Summary: CS2 API client: Counter-Strike 2 live scores, match results, player stats, teams, rankings (world + VRS), events and skin prices. Standard library only.
5
+ Author: Cito API
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://cs2-api.org
8
+ Project-URL: Documentation, https://cs2-api.org/endpoints/
9
+ Project-URL: Source, https://github.com/achillesscriptsvip/cs2-api
10
+ Project-URL: Issues, https://github.com/achillesscriptsvip/cs2-api/issues
11
+ Project-URL: Get a free API key, https://citoapi.com/signup?game=cs2&utm_source=pypi&utm_medium=referral&utm_campaign=cs2-api-package
12
+ Keywords: cs2,cs2-api,counter-strike,counter-strike-2,csgo,hltv,hltv-api,esports,skins,skin-prices,api-client
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Games/Entertainment
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # cs2-api: Counter-Strike 2 API client for JavaScript and Python
25
+
26
+ [![npm](https://img.shields.io/npm/v/cs2-api)](https://www.npmjs.com/package/cs2-api) [![PyPI](https://img.shields.io/pypi/v/cs2-api-client)](https://pypi.org/project/cs2-api-client/) [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
27
+
28
+ This is the official client for the **CS2 API** at [cs2-api.org](https://cs2-api.org). It covers:
29
+
30
+ - live Counter-Strike 2 match scores and round-level scoreboards
31
+ - match results, schedules and streams
32
+ - player stats (rating, ADR, KAST, K/D) and leaderboards
33
+ - teams, rosters and head-to-head
34
+ - the world ranking and Valve Regional Standings (VRS)
35
+ - events, standings and brackets
36
+ - transfers
37
+ - **CS2 skin prices across 25+ marketplaces**, with price history
38
+
39
+ It has zero dependencies. The JavaScript client ships TypeScript types and works as ESM or CommonJS; the Python client needs only the standard library.
40
+
41
+ > **You need a free API key** (500 requests/month, no card): **[Get your key](https://citoapi.com/signup?game=cs2&utm_source=npm-pypi&utm_medium=referral&utm_campaign=cs2-api-package)**
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ npm install cs2-api
47
+ ```
48
+
49
+ ```bash
50
+ pip install cs2-api-client
51
+ ```
52
+
53
+ ## JavaScript / TypeScript
54
+
55
+ ```js
56
+ import { CS2Api } from "cs2-api"; // or: const { CS2Api } = require("cs2-api")
57
+
58
+ const cs2 = new CS2Api(); // reads process.env.CITO_API_KEY
59
+
60
+ const live = await cs2.live(); // every match being played right now
61
+ for (const m of live) {
62
+ console.log(`${m.team1Name} ${m.team1Score}-${m.team2Score} ${m.team2Name} · ${m.currentMap}`);
63
+ }
64
+
65
+ const ranking = await cs2.worldRanking({ limit: 10 });
66
+ const vitality = await cs2.team("vitality"); // roster + recent results
67
+ const donk = await cs2.player("donk");
68
+ const prices = await cs2.skinPrices("ak-47-redline-field-tested-1ab1a2dc");
69
+ ```
70
+
71
+ ## Python
72
+
73
+ ```python
74
+ from cs2_api import CS2Api
75
+
76
+ cs2 = CS2Api() # reads CITO_API_KEY
77
+
78
+ for m in cs2.live():
79
+ print(f"{m['team1Name']} {m['team1Score']}-{m['team2Score']} {m['team2Name']} · {m['currentMap']}")
80
+
81
+ ranking = cs2.world_ranking(limit=10)
82
+ vitality = cs2.team("vitality")
83
+ donk = cs2.player("donk")
84
+ prices = cs2.skin_prices("ak-47-redline-field-tested-1ab1a2dc")
85
+ ```
86
+
87
+ ## Methods
88
+
89
+ JavaScript uses camelCase and Python uses snake_case (`worldRanking` / `world_ranking`). Teams, players and events accept an id or a slug (`vitality`, `donk`).
90
+
91
+ | Area | Methods |
92
+ |---|---|
93
+ | Live | `live()`, `liveScoreboard(matchId)`: sides, round, player K/D/ADR |
94
+ | Matches | `upcoming()`, `results()`, `today()`, `match(id)`, `matchMaps(id)`, `matchPlayerStats(id)`, `matchStreams(id)` |
95
+ | Teams | `team(slug)`, `teamMatches(slug)`, `teamStats(slug)`, `teamMapStats(slug)`, `headToHead(a, b)` |
96
+ | Players | `player(slug)`, `searchPlayers(q)`, `playerStats(slug)`, `playerMatches(slug)` |
97
+ | Events | `events({ status })`, `event(slug)`, `eventMatches(slug)`, `eventStandings(slug)`, `eventBracket(slug)` |
98
+ | Rankings & stats | `worldRanking()`, `vrsRanking()`, `leaderboard(metric)`, `transfers()`, `search(q)` |
99
+ | Skins | `searchSkins(q)`, `skin(slug)`, `skinPrices(slug)`, `skinHistory(slug)`, `trendingSkins()` |
100
+ | Anything else | `get(path, query, { raw })`: any of the 140+ endpoints, with `meta` for pagination when `raw: true` |
101
+
102
+ Errors throw `CS2ApiError` (JS) or raise `CS2ApiError` (Python), with `status` and the API's message.
103
+
104
+ ## Use cases
105
+
106
+ - **Discord bots**: [cs2-discord-bot](https://github.com/achillesscriptsvip/cs2-discord-bot)
107
+ - **Stream overlays**: [cs2-obs-overlay](https://github.com/achillesscriptsvip/cs2-obs-overlay)
108
+ - **Skin trading tools**: [cs2-skin-price-api](https://github.com/achillesscriptsvip/cs2-skin-price-api)
109
+ - **Moving off HLTV scrapers**: [hltv-api-alternative](https://github.com/achillesscriptsvip/hltv-api-alternative)
110
+ - Fantasy, analytics dashboards, prediction models and AI agents
111
+
112
+ ## Keep your key safe
113
+
114
+ Keep it in an environment variable or `.env` (git-ignored). Call the API from your server, never from browser code. If a key leaks, revoke it in your dashboard and create a new one.
115
+
116
+ ## Links
117
+
118
+ - CS2 API docs and endpoints: https://cs2-api.org/endpoints/
119
+ - Pricing: https://citoapi.com/pricing
120
+ - Issues: https://github.com/achillesscriptsvip/cs2-api/issues
121
+
122
+ Not affiliated with Valve, HLTV, ESL, BLAST or PGL. MIT licensed.
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/cs2_api/__init__.py
5
+ src/cs2_api/py.typed
6
+ src/cs2_api_client.egg-info/PKG-INFO
7
+ src/cs2_api_client.egg-info/SOURCES.txt
8
+ src/cs2_api_client.egg-info/dependency_links.txt
9
+ src/cs2_api_client.egg-info/top_level.txt
10
+ tests/test_live.py
@@ -0,0 +1,72 @@
1
+ """Live test of every method: CITO_API_KEY=... python -m unittest discover -s tests -v"""
2
+
3
+ import os
4
+ import unittest
5
+
6
+ from cs2_api import CS2Api, CS2ApiError
7
+
8
+ HAS_KEY = bool(os.environ.get("CITO_API_KEY"))
9
+
10
+
11
+ class NoKey(unittest.TestCase):
12
+ def test_missing_key_points_to_signup(self):
13
+ with self.assertRaises(CS2ApiError) as ctx:
14
+ CS2Api(api_key="")
15
+ self.assertIn("cs2-api.org", str(ctx.exception))
16
+
17
+
18
+ @unittest.skipUnless(HAS_KEY, "set CITO_API_KEY to run live tests")
19
+ class Live(unittest.TestCase):
20
+ def test_bad_key_is_401(self):
21
+ with self.assertRaises(CS2ApiError) as ctx:
22
+ CS2Api(api_key="cito_invalid").results(limit=1)
23
+ self.assertEqual(ctx.exception.status, 401)
24
+
25
+ def test_every_method_returns_data(self):
26
+ api = CS2Api()
27
+ results = api.results(limit=2)
28
+ mid = results[0]["id"]
29
+ ranking = api.world_ranking(limit=2)
30
+ a, b = (r["team"]["slug"] for r in ranking)
31
+ player = api.search_players("donk", limit=1)[0]
32
+ event = api.events(status="recent", limit=1)[0]
33
+ skin = api.search_skins("redline", limit=1)[0]
34
+ live = api.live()
35
+ calls = {
36
+ "live_scoreboard": api.live_scoreboard(live[0]["matchId"]) if live else "nothing live",
37
+ "upcoming": api.upcoming(limit=2),
38
+ "today": api.today(),
39
+ "match": api.match(mid),
40
+ "match_maps": api.match_maps(mid),
41
+ "match_player_stats": api.match_player_stats(mid),
42
+ "match_streams": api.match_streams(mid),
43
+ "team": api.team(a),
44
+ "team_matches": api.team_matches(a, limit=2),
45
+ "team_stats": api.team_stats(a),
46
+ "team_map_stats": api.team_map_stats(a),
47
+ "head_to_head": api.head_to_head(a, b),
48
+ "player": api.player(player["slug"]),
49
+ "player_stats": api.player_stats(player["slug"]),
50
+ "player_matches": api.player_matches(player["slug"], limit=2),
51
+ "event": api.event(event["slug"]),
52
+ "event_matches": api.event_matches(event["slug"]),
53
+ "event_standings": api.event_standings(event["slug"]),
54
+ "event_bracket": api.event_bracket(event["slug"]),
55
+ "vrs_ranking": api.vrs_ranking(limit=2),
56
+ "leaderboard": api.leaderboard("rating", limit=2),
57
+ "transfers": api.transfers(limit=2),
58
+ "search": api.search("vitality"),
59
+ "skin": api.skin(skin["slug"]),
60
+ "skin_prices": api.skin_prices(skin["slug"]),
61
+ "skin_history": api.skin_history(skin["slug"], range="30d"),
62
+ "trending_skins": api.trending_skins(limit=2),
63
+ }
64
+ for name, value in calls.items():
65
+ self.assertIsNotNone(value, name)
66
+ raw = api.get("/cs2/matches/results", {"limit": 1}, raw=True)
67
+ self.assertIn("meta", raw)
68
+ print(f"\n{len(calls) + 6} methods returned data")
69
+
70
+
71
+ if __name__ == "__main__":
72
+ unittest.main()