rivalsdata-api 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,22 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment:
11
+ name: pypi
12
+ url: https://pypi.org/p/rivalsdata-api
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: '3.x'
18
+ - run: python -m pip install build
19
+ - run: python -m build
20
+ - uses: pypa/gh-action-pypi-publish@release/v1
21
+ with:
22
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .pytest_cache/
8
+ .ruff_cache/
@@ -0,0 +1,88 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve rivalsdata-api. The client relies on undocumented
4
+ RivalsData endpoints, so contributions should keep requests conservative and
5
+ record how endpoint behavior was observed.
6
+
7
+ ## Development setup
8
+
9
+ Use an isolated virtual environment:
10
+
11
+ ```console
12
+ python -m venv .venv
13
+ # Windows PowerShell
14
+ .venv\Scripts\Activate.ps1
15
+ # macOS or Linux
16
+ source .venv/bin/activate
17
+ python -m pip install --upgrade pip
18
+ python -m pip install -e '.[dev]'
19
+ ```
20
+
21
+ Install the optional browser dependencies only when working on Camoufox:
22
+
23
+ ```console
24
+ python -m pip install -e '.[browser,dev]'
25
+ python -m camoufox fetch
26
+ ```
27
+
28
+ The browser extra does not include GeoIP support because the project does not
29
+ need geographic spoofing. A virtual environment also prevents this package's
30
+ dependencies from changing unrelated applications installed in system Python.
31
+
32
+ ## Project map
33
+
34
+ - `src/rivalsdata/client.py`: HTTP client, username resolution, UID lookup,
35
+ shared GET/POST handling, and the optional Camoufox retry.
36
+ - `src/rivalsdata/models.py`: mapping-compatible response models and the typed
37
+ `Player` wrapper.
38
+ - `src/rivalsdata/resources.py`: lazy player and site-wide endpoint resources.
39
+ - `src/rivalsdata/exceptions.py`: public exception types.
40
+ - `src/rivalsdata/__init__.py`: package exports and version.
41
+ - `pyproject.toml`: build backend, package metadata, runtime and optional
42
+ dependencies.
43
+ - `docs/API.md`: observed site sections, endpoint inventory, response samples,
44
+ and open questions.
45
+ - `docs/PROJECT_CONTEXT.md`: contributor and new-chat handoff notes.
46
+
47
+ ## How the client currently works
48
+
49
+ The client uses curl_cffi with browser TLS impersonation against
50
+ `https://api.rivalsdata.com`:
51
+
52
+ - `POST /players/search` with JSON `{"name": "..."}` returns search
53
+ suggestions. Search records include an `aid`, such as
54
+ `11001_1970288503`; the final numeric segment is the profile UID.
55
+ - `POST /player` with JSON `{"uid": 1970288503}` returns a typed `Player`
56
+ object. Profile keys work as both mapping values and attributes.
57
+ - Resource managers cover public leaderboard, hero, team-up, insight, faction,
58
+ match, and player-tab endpoints. See `docs/API.md` for the current inventory.
59
+ - If these requests are blocked and `use_browser_fallback=True`, the client
60
+ loads RivalsData in Camoufox and retries the POST from the page context.
61
+
62
+ These are observed implementation details, not a supported RivalsData contract.
63
+ Do not assume that `aid` formats, filters, routes, or JSON fields are permanent.
64
+
65
+ ## Contribution workflow
66
+
67
+ 1. Check the existing public API and exception behavior before changing it.
68
+ 2. Keep changes focused, typed, and documented. Avoid adding new runtime
69
+ dependencies unless the feature needs them.
70
+ 3. For endpoint changes, note the page or action that exposed the route and
71
+ the request shape. Never commit cookies, tokens, or browser profile data.
72
+ 4. Add or update automated tests for behavior changes. Keep network-dependent
73
+ checks opt-in; routine tests should use mocked HTTP responses. The initial
74
+ repository does not yet include an automated test suite.
75
+ 5. Run the checks for the code you changed:
76
+
77
+ ```console
78
+ python -m pytest
79
+ ruff check .
80
+ python -m build
81
+ ```
82
+
83
+ 6. Update the README and project context if user-facing methods, dependencies,
84
+ routes, or setup steps changed.
85
+
86
+ There is no live API compatibility guarantee. If RivalsData changes a route,
87
+ prefer a clear typed error over returning an empty profile or silently
88
+ mislabeling a search result.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rivalsdata-api contributors
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,189 @@
1
+ Metadata-Version: 2.5
2
+ Name: rivalsdata-api
3
+ Version: 1.0.0
4
+ Summary: Python client and MCP server for public Marvel Rivals stats from RivalsData
5
+ Project-URL: Homepage, https://github.com/GS-Rionnag/rivalsdata-api
6
+ Project-URL: Repository, https://github.com/GS-Rionnag/rivalsdata-api
7
+ Project-URL: Issues, https://github.com/GS-Rionnag/rivalsdata-api/issues
8
+ Project-URL: Changelog, https://github.com/GS-Rionnag/rivalsdata-api/releases
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api-client,game-stats,marvel-rivals,mcp,rivalsdata
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Games/Entertainment
22
+ Classifier: Topic :: Internet :: WWW/HTTP
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: curl-cffi>=0.7
25
+ Provides-Extra: browser
26
+ Requires-Dist: camoufox>=0.5.6; extra == 'browser'
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2; extra == 'dev'
29
+ Requires-Dist: pytest>=8; extra == 'dev'
30
+ Requires-Dist: ruff>=0.11; extra == 'dev'
31
+ Provides-Extra: mcp
32
+ Requires-Dist: mcp-ui-server>=1.0.0; extra == 'mcp'
33
+ Requires-Dist: mcp<2,>=1.12; extra == 'mcp'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # rivalsdata-api
37
+
38
+ An unofficial Python client for RivalsData's public Marvel Rivals data. It
39
+ uses the site's undocumented API, so routes and fields can change. The client
40
+ keeps unknown response fields accessible instead of discarding them.
41
+
42
+ ## Install
43
+
44
+ Python 3.10 or newer:
45
+
46
+ ```console
47
+ python -m pip install rivalsdata-api
48
+ ```
49
+
50
+ For editable development, clone the repository and run
51
+ `python -m pip install -e '.[dev]'`. The optional Camoufox Cloudflare fallback
52
+ is installed with `python -m pip install 'rivalsdata-api[browser]'`, followed
53
+ by `python -m camoufox fetch`.
54
+
55
+ ## Quick start
56
+
57
+ ```python
58
+ from rivalsdata import RivalsDataClient
59
+
60
+ with RivalsDataClient() as rd:
61
+ player = rd.get_player("GS-") # numeric UID works too
62
+ print(player.name, player.level, player.rank_game_season)
63
+
64
+ # Player profile sections are lazy resource managers.
65
+ hero_season = player.heroes.fetch(season=20)
66
+ map_stats = player.stats.maps(season=20)
67
+ match_page = player.matches.fetch(season=20)
68
+
69
+ # Current match (None if the profile is not currently in a game).
70
+ live_game = player.live_game.fetch()
71
+ if live_game is not None:
72
+ print(live_game.players, live_game.team_avg_rank)
73
+
74
+ # Site-wide resources are available from the client.
75
+ leaderboard = rd.leaderboards.fetch(limit=100, season=20, platform=1)
76
+ tier_list = rd.heroes.tier_list(platform=1, rank="grandmaster_plus")
77
+ team_ups = rd.team_ups.fetch(platform=1, rank="grandmaster_plus")
78
+ xp_page = rd.insights.xp()
79
+
80
+ print(hero_season[0].win_rate) # integer percent when wins/losses are present
81
+ ```
82
+
83
+ ## MCP server (ChatGPT and Claude)
84
+
85
+ Install the MCP extra and the package:
86
+
87
+ ```console
88
+ python -m pip install 'rivalsdata-api[mcp]'
89
+ ```
90
+
91
+ The server exposes read-only tools for player search and profiles, a player's
92
+ current live match (when they are in one), match history, player stats,
93
+ leaderboards, heroes, team-ups, public insights, matches, and factions. The
94
+ `show_player_dashboard` tool also returns an MCP-UI player report with rank and
95
+ competitive record, current match roster split by side, a hero win-rate chart,
96
+ and recent match form with K/D/A. It supports local stdio for Claude Desktop
97
+ and Streamable HTTP for remote MCP clients such as ChatGPT. Data comes from
98
+ RivalsData's undocumented API and may change; profile match history can be
99
+ private.
100
+
101
+ ### How the MCP UI works
102
+
103
+ `show_player_dashboard` fetches current data, then returns an HTML UI resource
104
+ alongside the tool result. MCP-UI labels it with a `ui://` resource URI and
105
+ preferred size. A compatible host can render that resource in a sandboxed
106
+ panel; a host without UI support can still use the regular MCP tools and their
107
+ text/data responses. ChatGPT uses MCP-UI's Apps SDK adapter, while Claude is
108
+ listed as supporting MCP Apps directly. The dashboard is a snapshot from the
109
+ time the tool runs; ask for it again to refresh.
110
+
111
+ ### Claude Desktop (local)
112
+
113
+ Add a server entry to Claude Desktop's `claude_desktop_config.json`, replacing
114
+ the path with the Python executable in the environment where the extra is
115
+ installed:
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "rivalsdata": {
121
+ "command": "C:\\path\\to\\venv\\Scripts\\python.exe",
122
+ "args": ["-m", "rivalsdata.mcp_server"]
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ On macOS/Linux, use the environment's `bin/python` path. Restart Claude Desktop
129
+ after saving the configuration.
130
+
131
+ ### ChatGPT or remote Claude connector
132
+
133
+ Run the server on a host reachable over HTTPS:
134
+
135
+ ```console
136
+ rivalsdata-mcp --transport streamable-http --host 0.0.0.0 --port 8000
137
+ ```
138
+
139
+ The MCP endpoint is `/mcp` (for example, `https://your-host.example/mcp`). Add
140
+ that endpoint through the client's custom/remote MCP connector settings. The
141
+ server does not implement authentication; put it behind an authenticated
142
+ HTTPS gateway before exposing it publicly. For local development, bind to
143
+ `127.0.0.1` instead. Use `python -m rivalsdata.mcp_server --help` to see options.
144
+
145
+ `Player` and returned `DataModel` objects support both mapping access and
146
+ attribute access (`player["level"]` or `player.level`). Nested dictionaries
147
+ and arrays are wrapped recursively; `.raw` returns a shallow copy of a model's
148
+ original JSON. For endpoints whose fields evolve, these generic typed wrappers
149
+ preserve the complete payload.
150
+
151
+ ## Public resources
152
+
153
+ - `rd.leaderboards.fetch(...)` — global player ranking.
154
+ - `rd.heroes.tier_list(...)`, `.get(hero_id)`, `.meta(hero_id, range=90)`,
155
+ `.leaderboard(hero_id, **filters)` — hero metrics and ranking.
156
+ - `rd.team_ups.fetch(...)` — team-up stats.
157
+ - `rd.insights.punishments(...)`, `.xp(...)`, `.top_500(...)`, `.commbans(...)`,
158
+ `.leavers(...)` — public insights and cursor metadata.
159
+ - `rd.factions.get(faction_id)`, `rd.matches.get(match_id)`,
160
+ `rd.profiles.get(username)`, and `rd.favorites.fetch(uids)` — detail/profile
161
+ lookups.
162
+ - `player.heroes.fetch(...)`, `.matches.fetch(...)`, `.live_game.fetch()`, `.teammates.fetch(...)`,
163
+ `.crosshairs.fetch()`, `.proficiency.fetch()`, `.punishments.fetch()`,
164
+ `.name_history.fetch()` — profile sections.
165
+ - `player.stats.heroes(...)`, `.maps(...)`, `.bans(...)` — detailed profile stats.
166
+
167
+ See [the observed API inventory](docs/API.md) for methods, parameters, observed
168
+ response shapes, and endpoints that require a RivalsData account. The API
169
+ inventory distinguishes observed behavior from inferred/unverified details.
170
+
171
+ ## Cloudflare fallback
172
+
173
+ Requests use curl_cffi with a Chrome TLS profile by default. If blocked, enable
174
+ the optional browser fallback:
175
+
176
+ ```python
177
+ with RivalsDataClient(use_browser_fallback=True) as rd:
178
+ player = rd.get_player(1970288503)
179
+ ```
180
+
181
+ ## Errors and contributions
182
+
183
+ All package exceptions inherit from `RivalsDataError`. See
184
+ [CONTRIBUTING.md](CONTRIBUTING.md) for setup, code layout, change workflow, and
185
+ notes for new contributors. [docs/PROJECT_CONTEXT.md](docs/PROJECT_CONTEXT.md)
186
+ is the handoff document for new coding sessions.
187
+
188
+ This project is not affiliated with RivalsData, NetEase, or Marvel. Keep
189
+ request rates reasonable and respect the site's terms.
@@ -0,0 +1,154 @@
1
+ # rivalsdata-api
2
+
3
+ An unofficial Python client for RivalsData's public Marvel Rivals data. It
4
+ uses the site's undocumented API, so routes and fields can change. The client
5
+ keeps unknown response fields accessible instead of discarding them.
6
+
7
+ ## Install
8
+
9
+ Python 3.10 or newer:
10
+
11
+ ```console
12
+ python -m pip install rivalsdata-api
13
+ ```
14
+
15
+ For editable development, clone the repository and run
16
+ `python -m pip install -e '.[dev]'`. The optional Camoufox Cloudflare fallback
17
+ is installed with `python -m pip install 'rivalsdata-api[browser]'`, followed
18
+ by `python -m camoufox fetch`.
19
+
20
+ ## Quick start
21
+
22
+ ```python
23
+ from rivalsdata import RivalsDataClient
24
+
25
+ with RivalsDataClient() as rd:
26
+ player = rd.get_player("GS-") # numeric UID works too
27
+ print(player.name, player.level, player.rank_game_season)
28
+
29
+ # Player profile sections are lazy resource managers.
30
+ hero_season = player.heroes.fetch(season=20)
31
+ map_stats = player.stats.maps(season=20)
32
+ match_page = player.matches.fetch(season=20)
33
+
34
+ # Current match (None if the profile is not currently in a game).
35
+ live_game = player.live_game.fetch()
36
+ if live_game is not None:
37
+ print(live_game.players, live_game.team_avg_rank)
38
+
39
+ # Site-wide resources are available from the client.
40
+ leaderboard = rd.leaderboards.fetch(limit=100, season=20, platform=1)
41
+ tier_list = rd.heroes.tier_list(platform=1, rank="grandmaster_plus")
42
+ team_ups = rd.team_ups.fetch(platform=1, rank="grandmaster_plus")
43
+ xp_page = rd.insights.xp()
44
+
45
+ print(hero_season[0].win_rate) # integer percent when wins/losses are present
46
+ ```
47
+
48
+ ## MCP server (ChatGPT and Claude)
49
+
50
+ Install the MCP extra and the package:
51
+
52
+ ```console
53
+ python -m pip install 'rivalsdata-api[mcp]'
54
+ ```
55
+
56
+ The server exposes read-only tools for player search and profiles, a player's
57
+ current live match (when they are in one), match history, player stats,
58
+ leaderboards, heroes, team-ups, public insights, matches, and factions. The
59
+ `show_player_dashboard` tool also returns an MCP-UI player report with rank and
60
+ competitive record, current match roster split by side, a hero win-rate chart,
61
+ and recent match form with K/D/A. It supports local stdio for Claude Desktop
62
+ and Streamable HTTP for remote MCP clients such as ChatGPT. Data comes from
63
+ RivalsData's undocumented API and may change; profile match history can be
64
+ private.
65
+
66
+ ### How the MCP UI works
67
+
68
+ `show_player_dashboard` fetches current data, then returns an HTML UI resource
69
+ alongside the tool result. MCP-UI labels it with a `ui://` resource URI and
70
+ preferred size. A compatible host can render that resource in a sandboxed
71
+ panel; a host without UI support can still use the regular MCP tools and their
72
+ text/data responses. ChatGPT uses MCP-UI's Apps SDK adapter, while Claude is
73
+ listed as supporting MCP Apps directly. The dashboard is a snapshot from the
74
+ time the tool runs; ask for it again to refresh.
75
+
76
+ ### Claude Desktop (local)
77
+
78
+ Add a server entry to Claude Desktop's `claude_desktop_config.json`, replacing
79
+ the path with the Python executable in the environment where the extra is
80
+ installed:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "rivalsdata": {
86
+ "command": "C:\\path\\to\\venv\\Scripts\\python.exe",
87
+ "args": ["-m", "rivalsdata.mcp_server"]
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ On macOS/Linux, use the environment's `bin/python` path. Restart Claude Desktop
94
+ after saving the configuration.
95
+
96
+ ### ChatGPT or remote Claude connector
97
+
98
+ Run the server on a host reachable over HTTPS:
99
+
100
+ ```console
101
+ rivalsdata-mcp --transport streamable-http --host 0.0.0.0 --port 8000
102
+ ```
103
+
104
+ The MCP endpoint is `/mcp` (for example, `https://your-host.example/mcp`). Add
105
+ that endpoint through the client's custom/remote MCP connector settings. The
106
+ server does not implement authentication; put it behind an authenticated
107
+ HTTPS gateway before exposing it publicly. For local development, bind to
108
+ `127.0.0.1` instead. Use `python -m rivalsdata.mcp_server --help` to see options.
109
+
110
+ `Player` and returned `DataModel` objects support both mapping access and
111
+ attribute access (`player["level"]` or `player.level`). Nested dictionaries
112
+ and arrays are wrapped recursively; `.raw` returns a shallow copy of a model's
113
+ original JSON. For endpoints whose fields evolve, these generic typed wrappers
114
+ preserve the complete payload.
115
+
116
+ ## Public resources
117
+
118
+ - `rd.leaderboards.fetch(...)` — global player ranking.
119
+ - `rd.heroes.tier_list(...)`, `.get(hero_id)`, `.meta(hero_id, range=90)`,
120
+ `.leaderboard(hero_id, **filters)` — hero metrics and ranking.
121
+ - `rd.team_ups.fetch(...)` — team-up stats.
122
+ - `rd.insights.punishments(...)`, `.xp(...)`, `.top_500(...)`, `.commbans(...)`,
123
+ `.leavers(...)` — public insights and cursor metadata.
124
+ - `rd.factions.get(faction_id)`, `rd.matches.get(match_id)`,
125
+ `rd.profiles.get(username)`, and `rd.favorites.fetch(uids)` — detail/profile
126
+ lookups.
127
+ - `player.heroes.fetch(...)`, `.matches.fetch(...)`, `.live_game.fetch()`, `.teammates.fetch(...)`,
128
+ `.crosshairs.fetch()`, `.proficiency.fetch()`, `.punishments.fetch()`,
129
+ `.name_history.fetch()` — profile sections.
130
+ - `player.stats.heroes(...)`, `.maps(...)`, `.bans(...)` — detailed profile stats.
131
+
132
+ See [the observed API inventory](docs/API.md) for methods, parameters, observed
133
+ response shapes, and endpoints that require a RivalsData account. The API
134
+ inventory distinguishes observed behavior from inferred/unverified details.
135
+
136
+ ## Cloudflare fallback
137
+
138
+ Requests use curl_cffi with a Chrome TLS profile by default. If blocked, enable
139
+ the optional browser fallback:
140
+
141
+ ```python
142
+ with RivalsDataClient(use_browser_fallback=True) as rd:
143
+ player = rd.get_player(1970288503)
144
+ ```
145
+
146
+ ## Errors and contributions
147
+
148
+ All package exceptions inherit from `RivalsDataError`. See
149
+ [CONTRIBUTING.md](CONTRIBUTING.md) for setup, code layout, change workflow, and
150
+ notes for new contributors. [docs/PROJECT_CONTEXT.md](docs/PROJECT_CONTEXT.md)
151
+ is the handoff document for new coding sessions.
152
+
153
+ This project is not affiliated with RivalsData, NetEase, or Marvel. Keep
154
+ request rates reasonable and respect the site's terms.
@@ -0,0 +1,90 @@
1
+ # RivalsData API inventory
2
+
3
+ This is an observed inventory of the public web client's API, gathered by
4
+ reviewing the RivalsData UI and its browser requests on 2026-09-29. The upstream
5
+ API is undocumented and can change. Field sets below are examples from live
6
+ responses, not schemas guaranteed by RivalsData. Response bodies are returned
7
+ as attribute-accessible `DataModel` / `StatRecord` objects, preserving unknown
8
+ keys.
9
+
10
+ ## Implemented public read endpoints
11
+
12
+ | Site section | HTTP endpoint | Parameters sent by this client | Observed response |
13
+ | --- | --- | --- | --- |
14
+ | Search | `POST /players/search` | `{"name": name}` | Search rows; `aid` may contain the numeric UID after the final `_`. |
15
+ | Player overview | `POST /player` | `{"uid": number}` | Object keys observed: `cached_at`, `claimed`, `faction`, `icon`, `last_seen`, `leaderboard`, `level`, `login_os`, `match_history_is_visible`, `mood`, `name`, `rank_game_season`, `status`, `uid`, `xp`. `rank_game_season` is keyed by game/season ids; competitive rows include `battle_count`, `rank_game_id`, `rank_score`, and `win_count`. |
16
+ | Player live game | `POST /live` | `{"match_id": status.battle_id, "uid": number}` | Object keys observed: `players` (12 player entries keyed by team slot) and `team_avg_rank` (rank averages keyed by side). Player rows include `ai`, `games`, `icon`, `losses`, `name`, `proficiency`, `rank`, `side`, `team_id`, `top_heroes`, `uid`, and `wins`. The match ID comes from the player's current `/player` response; the endpoint was observed on a profile marked `In game (Competitive)`. |
17
+ | Player hero summary | `POST /player/heroes` | `uid`, optional `season` | Array rows: `assists`, `deaths`, `games`, `hero_id`, `kda`, `kills`, `losses`, `rank`, `wins`. |
18
+ | Player crosshairs | `POST /player/crosshairs` | `uid` | Array rows: `crosshair`, `uses`. |
19
+ | Player match history | `POST /player/matches/cached` (or `/player/matches`) | `uid`, `cursor`, optional `season`; cached endpoint also accepts `mode`, `hero`, `teammate` | Cached response keys observed: `matches`, `next_cursor`, `source`; match rows include `assists`, `deaths`, `game_mode_id`, `game_play_mode_id`, `hero_id`, `is_mvp`, `is_svp`, `is_win`, `kills`, `map_id`, `match_uid`, `os`, `placement`, `platform`, `rank_level`, `rank_score`, `score_change`, `season`, `team_score`, `timestamp`, `winner_camp`. A sample profile marked its history private; a private profile can return no visible match rows. |
20
+ | Player teammates | `POST /player/teammates` | `uid`, optional `season`, `mode` | Array rows: `games`, `icon`, `losses`, `name`, `teammate_uid`, `wins`. |
21
+ | Player proficiency | `POST /player/proficiency` | `uid` | Object keyed by account id (sample: `11001_{uid}`); inner structure varies. |
22
+ | Player hero stats | `POST /player/stats/heroes` | `uid`, optional `season` | Array rows: `competitive`, `hero_id`, `quickplay`, `rank`. |
23
+ | Player map stats | `POST /player/stats/maps` | `uid`, optional `season` | Array shape varies; captured from the profile Stats tab. |
24
+ | Player ban stats | `POST /player/stats/bans` | `uid`, optional `season` | Array shape varies; captured from the profile Stats tab. |
25
+ | Player punishments | `POST /player/punishments` | `uid` | Object keys observed: `chat`, `login`, `rank`. |
26
+ | Player name history | `POST /player/name-history` | `uid` | Array rows: `first_seen`, `name`. |
27
+ | Global leaderboard | `GET /leaderboards` | `limit`, optional `skip`, `season`, `os` (Python `platform`) | Object keys observed: `count`, `players`, `updated_at`; row keys: `heroes`, `icon`, `losses`, `name`, `os`, `position`, `rank_level`, `rank_score`, `season`, `status`, `uid`, `wins`. |
28
+ | Hero tier list | `GET /stats/tierlist` | `platform`, `rank` | Hero rows displayed with tier, win rate, pick rate, ban rate, and games; raw row keys preserved. |
29
+ | Hero detail | `GET /stats/heroes/{hero_id}` | Path parameter | Hero aggregate object; full key set varies by hero and season. |
30
+ | Hero trend/meta | `GET /stats/meta/{hero_id}` | `range` (30, 90, or 180 days) | Trend/analytics object; raw keys preserved. |
31
+ | Hero leaderboard | `GET /stats/leaderboards` | `hero` plus caller-supplied query filters | Used by hero leaderboard pages. Parameter combinations and response fields are not fully verified. |
32
+ | Team-up stats | `GET /stats/teamups` | `platform`, `rank`, optional `hero` | Team-up usage and win-rate rows; raw keys preserved. |
33
+ | Punishments log | `GET /stats/punishments` | `kind`, optional `cursor` | Object: `last_update`, `next`, `results`; sample row keys: `expires_at`, `icon`, `issued_at`, `kind`, `name`, `peak_rank_level`, `peak_rank_score`, `rank`, `reason`, `uid`. |
34
+ | XP leaderboard | `GET /stats/xp` | Optional `cursor` | Object: `last_update`, `next`, `results`; sample row keys: `icon`, `name`, `rank`, `uid`, `xp`. |
35
+ | Top 500 finishes | `GET /stats/oaa` | `os` (Python `platform`) | Object: `count`, `last_update`, `os`, `players`; sample row keys: `avg_placement`, `avg_score`, `finishes`, `icon`, `name`, `seasons`, `uid`. |
36
+ | Hero comm-ban insight | `GET /stats/commbans` | `mode` (`all` or `competitive`) | Object: `heroes`, `last_update`, `mode`, `overall_pct`; hero rows: `ci95`, `hero_id`, `pct`, `qualifying_players`, `vs_avg`, `weighted_banned`, `weighted_players`. |
37
+ | Hero AFK insight | `GET /stats/leavers` | `mode` (`all` or `competitive`) | Object: `heroes`, `last_update`, `mode`, `overall_pct`; hero rows: `ci95`, `games`, `hero_id`, `leaves`, `leaves_per_player`, `pct`, `players`, `vs_avg`. |
38
+ | Faction details | `GET /faction/{faction_id}` | Path parameter | Faction overview, public profile/member list, and results; nested schema varies. |
39
+ | Match details | `POST /match` | `{"match_id": "..."}` | Object keys observed: `match_uid`, `replay_id`, `winner_camp`, `duration_seconds`, `map_id`, `game_mode_id`, `game_play_mode_id`, `platform`, `timestamp`, `draft`, `teams`; team player rows include combat stats and per-hero usage. |
40
+ | Public profile card | `GET /profiles/{username}` | Username path parameter | Route observed in the profile frontend; complete response schema not captured. |
41
+ | Favorites lookup | `POST /favorites` | `{"uids": [uid, ...]}` | Request observed in frontend assets; response schema not captured. |
42
+
43
+ The client exposes these read resources through `RivalsDataClient` and `Player`;
44
+ see README examples and method docstrings. `DataModel.win_rate` returns an
45
+ integer percentage from a direct win-rate field, `wins`/`losses`, or the
46
+ competitive profile row's `win_count`/`battle_count`. If the source provides
47
+ none of these, it returns `None`.
48
+
49
+ ## Observed UI and routes
50
+
51
+ | UI route | What the UI exposes |
52
+ | --- | --- |
53
+ | `/` | Search, top leaderboard, favorites, top heroes, and top team-ups. |
54
+ | `/leaderboard` | Name filter, online-only switch, platform, season, refresh, and player rows. |
55
+ | `/stats` | Hero tier list with rank/platform/role filters and tier/win/pick/ban/games sorting. |
56
+ | `/team-ups` | Team-up pairings with hero, rank, and platform filters. |
57
+ | `/insights/punishments`, `/insights/xp`, `/insights/oaa`, `/insights/toxicity`, `/insights/afk` | Punishment log, XP ranking, Top 500 finishes, hero comm-ban rate, and hero AFK rate. |
58
+ | `/player/{uid}` | Overview, hero summaries, teammates, crosshairs, Match History, Live Game, Proficiency, Stats, Punishments, and Name History tabs. Match History may be private. |
59
+ | `/heroes/{slug}/stats` | Hero statistics and selectable 30/90/180-day charts. |
60
+ | `/heroes/{slug}/leaderboard` | Hero-specific ranked players. |
61
+ | `/heroes/{slug}/counters` and `/heroes/{slug}/synergy` | The UI displayed “Coming Soon” during inspection; no public data call was observed. |
62
+ | `/factions/{faction_id}` | Faction description and members. |
63
+ | `/matches/{match_id}` | Match detail route linked from an expanded public match-history card. |
64
+ | `/profiles` | Account-facing profile management page. |
65
+
66
+ The Live Game tab requests `POST /live` for profiles whose `status.battle_id`
67
+ is set. The client exposes this as `player.live_game.fetch()`. Since game status
68
+ can change, load a fresh player profile before fetching; the method returns
69
+ `None` when that profile has no active battle ID.
70
+
71
+ ## Account actions and unresolved endpoints
72
+
73
+ Frontend assets also reference `GET /profiles`, `POST /profiles/{username}/edit`,
74
+ `POST /bind/start`, and `POST /bind/check`. These are associated with profile
75
+ management and account linking. Their complete request/response contracts and
76
+ authentication behavior were not established. Profile edit and bind routes
77
+ can modify account state; they are documented here for completeness but are
78
+ not implemented as public methods. `GET /profiles/{username}` and
79
+ `POST /favorites` have thin read wrappers, but their response schemas remain
80
+ unverified.
81
+
82
+ Other open research items:
83
+
84
+ - Capture response bodies for the remaining Insights mode/filter variants.
85
+ - Verify the hero leaderboard query parameters and faction response shape from
86
+ real visible links.
87
+ - Determine whether match-history cursor pagination and the uncached route
88
+ remain available for accounts that have made history public.
89
+ - Recheck this inventory when the site changes. Do not infer a supported API
90
+ contract from a route string alone.