rivalsdata-api 1.1.0__tar.gz → 1.2.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.
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/PKG-INFO +33 -1
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/README.md +176 -144
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/docs/API.md +36 -1
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/docs/PROJECT_CONTEXT.md +20 -5
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/pyproject.toml +1 -1
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/__init__.py +10 -2
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/hero_ids.py +29 -2
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/mcp_server.py +440 -436
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/models.py +41 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/resources.py +63 -3
- rivalsdata_api-1.2.0/tests/test_class_stats.py +57 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/.github/workflows/publish-pypi.yml +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/.gitignore +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/CONTRIBUTING.md +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/LICENSE +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/client.py +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/src/rivalsdata/exceptions.py +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/tests/test_hero_names.py +0 -0
- {rivalsdata_api-1.1.0 → rivalsdata_api-1.2.0}/tests/test_typed_responses.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rivalsdata-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Python client and MCP server for public Marvel Rivals stats from RivalsData
|
|
5
5
|
Project-URL: Homepage, https://github.com/GS-Rionnag/rivalsdata-api
|
|
6
6
|
Project-URL: Repository, https://github.com/GS-Rionnag/rivalsdata-api
|
|
@@ -67,6 +67,7 @@ with RivalsDataClient() as rd:
|
|
|
67
67
|
|
|
68
68
|
# Player profile sections are lazy resource managers.
|
|
69
69
|
hero_season = player.heroes.fetch(season=20)
|
|
70
|
+
all_hero_seasons = player.heroes.fetch(season="all")
|
|
70
71
|
map_stats = player.stats.maps(season=20)
|
|
71
72
|
match_page = player.matches.fetch(season=20)
|
|
72
73
|
|
|
@@ -84,6 +85,37 @@ with RivalsDataClient() as rd:
|
|
|
84
85
|
print(hero_season[0].win_rate) # integer percent when wins/losses are present
|
|
85
86
|
```
|
|
86
87
|
|
|
88
|
+
Calculated player class statistics are available through
|
|
89
|
+
`player.stats.classes(season=20)` and the MCP `get_player_stats` tool with
|
|
90
|
+
`category="classes"`. Each row contains `player_class` (`tank`, `support`,
|
|
91
|
+
`dps`), the official role, hero IDs, and separate `competitive` and `quickplay`
|
|
92
|
+
totals for games, wins, losses, and available MVP/SVP counts.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
with RivalsDataClient() as rd:
|
|
96
|
+
stats = rd.get_player("GS-").stats.classes(season=20)
|
|
97
|
+
for row in stats.classes:
|
|
98
|
+
print(row.player_class, row.competitive.win_rate)
|
|
99
|
+
print(stats.excluded) # Unknown roles or incomplete win/loss records
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Win rates are `total wins / (total wins + total losses)`, rounded to an integer
|
|
103
|
+
percent. They are weighted by hero records, rather than averaging hero win
|
|
104
|
+
rates. Switching heroes can make one match contribute to multiple records;
|
|
105
|
+
these totals describe hero participation, not distinct matches. Empty modes
|
|
106
|
+
have a `None` win rate. Role mappings were observed on RivalsData on
|
|
107
|
+
2026-09-30, including Deadpool's separate role IDs; generic Deadpool and unknown
|
|
108
|
+
IDs are excluded rather than assigned a guessed class.
|
|
109
|
+
|
|
110
|
+
Character playtime was checked with Camoufox on 2026-09-30. Player hero stats
|
|
111
|
+
did not expose cumulative hours, including in All Seasons. Match details do
|
|
112
|
+
provide seconds in `match.teams[].players[].heroes[].play_time`; the site shows
|
|
113
|
+
these as minutes and seconds when hovering a hero portrait. Sum the relevant
|
|
114
|
+
player's entries across distinct retrieved matches and divide by 3600 to get
|
|
115
|
+
character hours for those matches. Incomplete history prevents treating this as
|
|
116
|
+
a lifetime total. See [the playtime investigation](docs/API.md#character-playtime-investigation-2026-09-30)
|
|
117
|
+
for the observed fields and example.
|
|
118
|
+
|
|
87
119
|
## MCP server (ChatGPT and Claude)
|
|
88
120
|
|
|
89
121
|
Install the MCP extra and the package:
|
|
@@ -1,70 +1,102 @@
|
|
|
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
|
|
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
23
|
from rivalsdata import RivalsDataClient, hero_id, hero_name
|
|
24
24
|
|
|
25
25
|
print(hero_name(1016)) # Loki
|
|
26
26
|
print(hero_id("Loki")) # 1016
|
|
27
|
-
|
|
28
|
-
with RivalsDataClient() as rd:
|
|
29
|
-
player = rd.get_player("GS-") # numeric UID works too
|
|
30
|
-
print(player.name, player.level, player.rank_game_season)
|
|
31
|
-
|
|
32
|
-
# Player profile sections are lazy resource managers.
|
|
33
|
-
hero_season = player.heroes.fetch(season=20)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
27
|
+
|
|
28
|
+
with RivalsDataClient() as rd:
|
|
29
|
+
player = rd.get_player("GS-") # numeric UID works too
|
|
30
|
+
print(player.name, player.level, player.rank_game_season)
|
|
31
|
+
|
|
32
|
+
# Player profile sections are lazy resource managers.
|
|
33
|
+
hero_season = player.heroes.fetch(season=20)
|
|
34
|
+
all_hero_seasons = player.heroes.fetch(season="all")
|
|
35
|
+
map_stats = player.stats.maps(season=20)
|
|
36
|
+
match_page = player.matches.fetch(season=20)
|
|
37
|
+
|
|
38
|
+
# Current match (None if the profile is not currently in a game).
|
|
39
|
+
live_game = player.live_game.fetch()
|
|
40
|
+
if live_game is not None:
|
|
41
|
+
print(live_game.players, live_game.team_avg_rank)
|
|
42
|
+
|
|
43
|
+
# Site-wide resources are available from the client.
|
|
44
|
+
leaderboard = rd.leaderboards.fetch(limit=100, season=20, platform=1)
|
|
45
|
+
tier_list = rd.heroes.tier_list(platform=1, rank="grandmaster_plus")
|
|
46
|
+
team_ups = rd.team_ups.fetch(platform=1, rank="grandmaster_plus")
|
|
47
|
+
xp_page = rd.insights.xp()
|
|
48
|
+
|
|
49
|
+
print(hero_season[0].win_rate) # integer percent when wins/losses are present
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Calculated player class statistics are available through
|
|
53
|
+
`player.stats.classes(season=20)` and the MCP `get_player_stats` tool with
|
|
54
|
+
`category="classes"`. Each row contains `player_class` (`tank`, `support`,
|
|
55
|
+
`dps`), the official role, hero IDs, and separate `competitive` and `quickplay`
|
|
56
|
+
totals for games, wins, losses, and available MVP/SVP counts.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
with RivalsDataClient() as rd:
|
|
60
|
+
stats = rd.get_player("GS-").stats.classes(season=20)
|
|
61
|
+
for row in stats.classes:
|
|
62
|
+
print(row.player_class, row.competitive.win_rate)
|
|
63
|
+
print(stats.excluded) # Unknown roles or incomplete win/loss records
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Win rates are `total wins / (total wins + total losses)`, rounded to an integer
|
|
67
|
+
percent. They are weighted by hero records, rather than averaging hero win
|
|
68
|
+
rates. Switching heroes can make one match contribute to multiple records;
|
|
69
|
+
these totals describe hero participation, not distinct matches. Empty modes
|
|
70
|
+
have a `None` win rate. Role mappings were observed on RivalsData on
|
|
71
|
+
2026-09-30, including Deadpool's separate role IDs; generic Deadpool and unknown
|
|
72
|
+
IDs are excluded rather than assigned a guessed class.
|
|
73
|
+
|
|
74
|
+
Character playtime was checked with Camoufox on 2026-09-30. Player hero stats
|
|
75
|
+
did not expose cumulative hours, including in All Seasons. Match details do
|
|
76
|
+
provide seconds in `match.teams[].players[].heroes[].play_time`; the site shows
|
|
77
|
+
these as minutes and seconds when hovering a hero portrait. Sum the relevant
|
|
78
|
+
player's entries across distinct retrieved matches and divide by 3600 to get
|
|
79
|
+
character hours for those matches. Incomplete history prevents treating this as
|
|
80
|
+
a lifetime total. See [the playtime investigation](docs/API.md#character-playtime-investigation-2026-09-30)
|
|
81
|
+
for the observed fields and example.
|
|
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 returns an MCP-UI player card with rank and
|
|
95
|
+
competitive record plus one optional data section per call: current match
|
|
96
|
+
roster, hero win-rate chart, or recent match form with K/D/A. This keeps each
|
|
97
|
+
dashboard pull to the profile plus at most one additional endpoint. It supports
|
|
98
|
+
local stdio for Claude Desktop
|
|
99
|
+
and Streamable HTTP for remote MCP clients such as ChatGPT. Data comes from
|
|
68
100
|
RivalsData's undocumented API and may change; profile match history can be
|
|
69
101
|
private.
|
|
70
102
|
|
|
@@ -75,9 +107,9 @@ a hero name or numeric ID. The pip package also exports `hero_name(id)` and
|
|
|
75
107
|
`.top_hero_name` conveniences. Those resolved fields are included in mapping
|
|
76
108
|
iteration and `.to_dict()` output to simplify serialization; `.raw` remains the
|
|
77
109
|
untouched source payload.
|
|
78
|
-
|
|
79
|
-
### How the MCP UI works
|
|
80
|
-
|
|
110
|
+
|
|
111
|
+
### How the MCP UI works
|
|
112
|
+
|
|
81
113
|
`show_player_dashboard` fetches current data, then returns an HTML UI resource
|
|
82
114
|
alongside the tool result. It advertises the dashboard through
|
|
83
115
|
`_meta.ui.resourceUri`, uses the `text/html;profile=mcp-app` resource MIME type,
|
|
@@ -88,44 +120,44 @@ also includes `openai/outputTemplate` as a ChatGPT compatibility alias. Hosts
|
|
|
88
120
|
without UI support still receive a text result and can use the regular MCP
|
|
89
121
|
tools. The dashboard is a snapshot from the time the tool runs; ask for it
|
|
90
122
|
again to refresh.
|
|
91
|
-
The `section` argument defaults to `live_match`; use `hero_form` or
|
|
92
|
-
`recent_matches` in separate calls when you need those views.
|
|
93
|
-
|
|
94
|
-
### Claude Desktop (local)
|
|
95
|
-
|
|
96
|
-
Add a server entry to Claude Desktop's `claude_desktop_config.json`, replacing
|
|
97
|
-
the path with the Python executable in the environment where the extra is
|
|
98
|
-
installed:
|
|
99
|
-
|
|
100
|
-
```json
|
|
101
|
-
{
|
|
102
|
-
"mcpServers": {
|
|
103
|
-
"rivalsdata": {
|
|
104
|
-
"command": "C:\\path\\to\\venv\\Scripts\\python.exe",
|
|
105
|
-
"args": ["-m", "rivalsdata.mcp_server"]
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
On macOS/Linux, use the environment's `bin/python` path. Restart Claude Desktop
|
|
112
|
-
after saving the configuration.
|
|
113
|
-
|
|
114
|
-
### ChatGPT or remote Claude connector
|
|
115
|
-
|
|
116
|
-
Run the server on a host reachable over HTTPS:
|
|
117
|
-
|
|
118
|
-
```console
|
|
119
|
-
uvicorn rivalsdata.mcp_server:app --host 0.0.0.0 --port 8000
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
The MCP endpoint is `/mcp` (for example, `https://your-host.example/mcp`). Add
|
|
123
|
-
that endpoint through the client's custom/remote MCP connector settings. The
|
|
124
|
-
server does not implement authentication; put it behind an authenticated
|
|
125
|
-
HTTPS gateway before exposing it publicly. For local development, bind to
|
|
126
|
-
`127.0.0.1` instead. The `app` is the MCP SDK's Streamable HTTP ASGI
|
|
127
|
-
application; Uvicorn manages its lifespan and session manager.
|
|
128
|
-
|
|
123
|
+
The `section` argument defaults to `live_match`; use `hero_form` or
|
|
124
|
+
`recent_matches` in separate calls when you need those views.
|
|
125
|
+
|
|
126
|
+
### Claude Desktop (local)
|
|
127
|
+
|
|
128
|
+
Add a server entry to Claude Desktop's `claude_desktop_config.json`, replacing
|
|
129
|
+
the path with the Python executable in the environment where the extra is
|
|
130
|
+
installed:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"rivalsdata": {
|
|
136
|
+
"command": "C:\\path\\to\\venv\\Scripts\\python.exe",
|
|
137
|
+
"args": ["-m", "rivalsdata.mcp_server"]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
On macOS/Linux, use the environment's `bin/python` path. Restart Claude Desktop
|
|
144
|
+
after saving the configuration.
|
|
145
|
+
|
|
146
|
+
### ChatGPT or remote Claude connector
|
|
147
|
+
|
|
148
|
+
Run the server on a host reachable over HTTPS:
|
|
149
|
+
|
|
150
|
+
```console
|
|
151
|
+
uvicorn rivalsdata.mcp_server:app --host 0.0.0.0 --port 8000
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The MCP endpoint is `/mcp` (for example, `https://your-host.example/mcp`). Add
|
|
155
|
+
that endpoint through the client's custom/remote MCP connector settings. The
|
|
156
|
+
server does not implement authentication; put it behind an authenticated
|
|
157
|
+
HTTPS gateway before exposing it publicly. For local development, bind to
|
|
158
|
+
`127.0.0.1` instead. The `app` is the MCP SDK's Streamable HTTP ASGI
|
|
159
|
+
application; Uvicorn manages its lifespan and session manager.
|
|
160
|
+
|
|
129
161
|
Every implemented response route now has named endpoint models and row models
|
|
130
162
|
with annotations for fields observed in the API inventory. This includes
|
|
131
163
|
`Player`, `Match`, `MatchHistory`, `MatchTeam`, `MatchPlayer`, `Character`,
|
|
@@ -151,43 +183,43 @@ with RivalsDataClient() as rd:
|
|
|
151
183
|
tier_list = rd.heroes.tier_list() # TierListResponse
|
|
152
184
|
print(tier_list.heroes[0].hero_id) # Character
|
|
153
185
|
```
|
|
154
|
-
|
|
155
|
-
## Public resources
|
|
156
|
-
|
|
157
|
-
- `rd.leaderboards.fetch(...)` — global player ranking.
|
|
158
|
-
- `rd.heroes.tier_list(...)`, `.get(hero_id)`, `.meta(hero_id, range=90)`,
|
|
159
|
-
`.leaderboard(hero_id, **filters)` — hero metrics and ranking.
|
|
160
|
-
- `rd.team_ups.fetch(...)` — team-up stats.
|
|
161
|
-
- `rd.insights.punishments(...)`, `.xp(...)`, `.top_500(...)`, `.commbans(...)`,
|
|
162
|
-
`.leavers(...)` — public insights and cursor metadata.
|
|
163
|
-
- `rd.factions.get(faction_id)`, `rd.matches.get(match_id)`,
|
|
164
|
-
`rd.profiles.get(username)`, and `rd.favorites.fetch(uids)` — detail/profile
|
|
165
|
-
lookups.
|
|
166
|
-
- `player.heroes.fetch(...)`, `.matches.fetch(...)`, `.live_game.fetch()`, `.teammates.fetch(...)`,
|
|
167
|
-
`.crosshairs.fetch()`, `.proficiency.fetch()`, `.punishments.fetch()`,
|
|
168
|
-
`.name_history.fetch()` — profile sections.
|
|
169
|
-
- `player.stats.heroes(...)`, `.maps(...)`, `.bans(...)` — detailed profile stats.
|
|
170
|
-
|
|
171
|
-
See [the observed API inventory](docs/API.md) for methods, parameters, observed
|
|
172
|
-
response shapes, and endpoints that require a RivalsData account. The API
|
|
173
|
-
inventory distinguishes observed behavior from inferred/unverified details.
|
|
174
|
-
|
|
175
|
-
## Cloudflare fallback
|
|
176
|
-
|
|
177
|
-
Requests use curl_cffi with a Chrome TLS profile by default. If blocked, enable
|
|
178
|
-
the optional browser fallback:
|
|
179
|
-
|
|
180
|
-
```python
|
|
181
|
-
with RivalsDataClient(use_browser_fallback=True) as rd:
|
|
182
|
-
player = rd.get_player(1970288503)
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## Errors and contributions
|
|
186
|
-
|
|
187
|
-
All package exceptions inherit from `RivalsDataError`. See
|
|
188
|
-
[CONTRIBUTING.md](CONTRIBUTING.md) for setup, code layout, change workflow, and
|
|
189
|
-
notes for new contributors. [docs/PROJECT_CONTEXT.md](docs/PROJECT_CONTEXT.md)
|
|
190
|
-
is the handoff document for new coding sessions.
|
|
191
|
-
|
|
192
|
-
This project is not affiliated with RivalsData, NetEase, or Marvel. Keep
|
|
193
|
-
request rates reasonable and respect the site's terms.
|
|
186
|
+
|
|
187
|
+
## Public resources
|
|
188
|
+
|
|
189
|
+
- `rd.leaderboards.fetch(...)` — global player ranking.
|
|
190
|
+
- `rd.heroes.tier_list(...)`, `.get(hero_id)`, `.meta(hero_id, range=90)`,
|
|
191
|
+
`.leaderboard(hero_id, **filters)` — hero metrics and ranking.
|
|
192
|
+
- `rd.team_ups.fetch(...)` — team-up stats.
|
|
193
|
+
- `rd.insights.punishments(...)`, `.xp(...)`, `.top_500(...)`, `.commbans(...)`,
|
|
194
|
+
`.leavers(...)` — public insights and cursor metadata.
|
|
195
|
+
- `rd.factions.get(faction_id)`, `rd.matches.get(match_id)`,
|
|
196
|
+
`rd.profiles.get(username)`, and `rd.favorites.fetch(uids)` — detail/profile
|
|
197
|
+
lookups.
|
|
198
|
+
- `player.heroes.fetch(...)`, `.matches.fetch(...)`, `.live_game.fetch()`, `.teammates.fetch(...)`,
|
|
199
|
+
`.crosshairs.fetch()`, `.proficiency.fetch()`, `.punishments.fetch()`,
|
|
200
|
+
`.name_history.fetch()` — profile sections.
|
|
201
|
+
- `player.stats.heroes(...)`, `.maps(...)`, `.bans(...)` — detailed profile stats.
|
|
202
|
+
|
|
203
|
+
See [the observed API inventory](docs/API.md) for methods, parameters, observed
|
|
204
|
+
response shapes, and endpoints that require a RivalsData account. The API
|
|
205
|
+
inventory distinguishes observed behavior from inferred/unverified details.
|
|
206
|
+
|
|
207
|
+
## Cloudflare fallback
|
|
208
|
+
|
|
209
|
+
Requests use curl_cffi with a Chrome TLS profile by default. If blocked, enable
|
|
210
|
+
the optional browser fallback:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
with RivalsDataClient(use_browser_fallback=True) as rd:
|
|
214
|
+
player = rd.get_player(1970288503)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Errors and contributions
|
|
218
|
+
|
|
219
|
+
All package exceptions inherit from `RivalsDataError`. See
|
|
220
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for setup, code layout, change workflow, and
|
|
221
|
+
notes for new contributors. [docs/PROJECT_CONTEXT.md](docs/PROJECT_CONTEXT.md)
|
|
222
|
+
is the handoff document for new coding sessions.
|
|
223
|
+
|
|
224
|
+
This project is not affiliated with RivalsData, NetEase, or Marvel. Keep
|
|
225
|
+
request rates reasonable and respect the site's terms.
|
|
@@ -16,7 +16,7 @@ and `.raw` so upstream additions are not discarded.
|
|
|
16
16
|
| Search | `POST /players/search` | `{"name": name}` | Search rows; `aid` may contain the numeric UID after the final `_`. |
|
|
17
17
|
| 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`. |
|
|
18
18
|
| 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)`. |
|
|
19
|
-
| Player hero summary | `POST /player/heroes` | `uid`, optional `season` | Array rows: `assists`, `deaths`, `games`, `hero_id`, `kda`, `kills`, `losses`, `rank`, `wins`. |
|
|
19
|
+
| Player hero summary | `POST /player/heroes` | `uid`, optional `season`; wrapper `season="all"` sends season ID `-1` | Array rows: `assists`, `deaths`, `games`, `hero_id`, `kda`, `kills`, `losses`, `rank`, `wins`. The all-seasons selector was verified against the public `GS-` profile; `season=-1` returned 40 hero rows, while `season=0` returned none. |
|
|
20
20
|
| Player crosshairs | `POST /player/crosshairs` | `uid` | Array rows: `crosshair`, `uses`. |
|
|
21
21
|
| 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. |
|
|
22
22
|
| Player teammates | `POST /player/teammates` | `uid`, optional `season`, `mode` | Array rows: `games`, `icon`, `losses`, `name`, `teammate_uid`, `wins`. |
|
|
@@ -50,6 +50,41 @@ none of these, it returns `None`.
|
|
|
50
50
|
|
|
51
51
|
## Observed UI and routes
|
|
52
52
|
|
|
53
|
+
`player.stats.classes(season=...)` derives tank (Vanguard), support (Strategist),
|
|
54
|
+
and DPS (Duelist) totals from `/player/stats/heroes`; it does not call a class
|
|
55
|
+
endpoint. It sums games/wins/losses separately for competitive and quickplay,
|
|
56
|
+
and MVP/SVP counts when every included row supplies them. Win rate is calculated
|
|
57
|
+
from summed wins and losses. Unknown classes and incomplete win/loss rows appear
|
|
58
|
+
in `excluded`. Hero switching can count a single match in multiple hero records,
|
|
59
|
+
so class totals are participation counts rather than distinct matches.
|
|
60
|
+
|
|
61
|
+
The roster at `/stats` on 2026-09-30 identifies Deadpool variants as 10571
|
|
62
|
+
(tank), 10572 (DPS), and 10573 (support), Daredevil as 1055, and Angela as 1056.
|
|
63
|
+
|
|
64
|
+
### Character playtime investigation (2026-09-30)
|
|
65
|
+
|
|
66
|
+
Using Camoufox (the package's optional browser dependency), the public GS-
|
|
67
|
+
profile (`1970288503`) was inspected in Stats > Heroes for the current season
|
|
68
|
+
and All Seasons. Expanded hero cards showed games, wins/losses, win rate, KDA,
|
|
69
|
+
accuracy, MVP/SVP counts, combat averages, and team-ups. Neither the UI nor the
|
|
70
|
+
captured `/player/heroes` and `/player/stats/heroes` responses supplied cumulative
|
|
71
|
+
hero playtime. The all-seasons stats request returned 49 hero rows, including
|
|
72
|
+
Deadpool's distinct role variants.
|
|
73
|
+
|
|
74
|
+
Match details do expose per-character time in seconds at
|
|
75
|
+
`teams[].players[].heroes[].play_time`. For match
|
|
76
|
+
`5518155_1790655030_1272083_11001_11`, the match duration was 584 seconds and GS-'s
|
|
77
|
+
Loki entry contained `play_time=583.8668914120644`. Hovering the hero portrait
|
|
78
|
+
displayed `Loki` and `9:43`. The observed frontend formatter takes
|
|
79
|
+
`floor(play_time / 60)` minutes and `floor(play_time % 60)` seconds.
|
|
80
|
+
|
|
81
|
+
Hours for a character over a supplied set of matches can be calculated by
|
|
82
|
+
summing that player's matching hero entries and dividing by 3600. Include all
|
|
83
|
+
matching entries (a hero can have multiple usage segments), and deduplicate
|
|
84
|
+
match IDs. This produces time for the retrieved matches, not a guaranteed
|
|
85
|
+
lifetime or season total: history and detail availability may be incomplete.
|
|
86
|
+
No automatic bulk match fetch was added as part of class statistics.
|
|
87
|
+
|
|
53
88
|
| UI route | What the UI exposes |
|
|
54
89
|
| --- | --- |
|
|
55
90
|
| `/` | Search, top leaderboard, favorites, top heroes, and top team-ups. |
|
|
@@ -15,7 +15,7 @@ as public, stable methods. Keep requests respectful and conservative.
|
|
|
15
15
|
## Current package
|
|
16
16
|
|
|
17
17
|
- Distribution: `rivalsdata-api`; import: `rivalsdata`.
|
|
18
|
-
- Version: `1.
|
|
18
|
+
- Version: `1.2.0`.
|
|
19
19
|
- Python `>=3.10`, Hatchling build, `src/` layout.
|
|
20
20
|
- Runtime HTTP dependency: `curl-cffi`; optional browser fallback: Camoufox.
|
|
21
21
|
- Public entry point: `RivalsDataClient`.
|
|
@@ -45,7 +45,7 @@ copy of the full response.
|
|
|
45
45
|
Lazy subresources include `player.heroes.fetch(...)`, `player.matches.fetch(...)`,
|
|
46
46
|
`player.teammates.fetch(...)`, `player.crosshairs.fetch()`,
|
|
47
47
|
`player.proficiency.fetch()`, `player.punishments.fetch()`,
|
|
48
|
-
`player.name_history.fetch()`, and `player.stats.heroes/maps/bans(...)`.
|
|
48
|
+
`player.name_history.fetch()`, and `player.stats.heroes/maps/bans/classes(...)`.
|
|
49
49
|
Client-wide resources include `client.leaderboards`, `client.heroes`,
|
|
50
50
|
`client.team_ups`, `client.insights`, `client.factions`, and `client.matches`.
|
|
51
51
|
`client.profiles` and `client.favorites` have typed read methods. The profile
|
|
@@ -54,6 +54,21 @@ Favorites requires numeric UIDs and returns player summary rows.
|
|
|
54
54
|
Rows offer `.win_rate` and `.winrate` integer-percent access when data supports
|
|
55
55
|
it; all original data remains in mapping access.
|
|
56
56
|
|
|
57
|
+
`player.stats.classes(season=...)` groups observed hero IDs into tank, support,
|
|
58
|
+
and DPS. It sums games, wins, losses, and available MVP/SVP counts separately
|
|
59
|
+
for competitive and quickplay, with win rates calculated from summed wins and
|
|
60
|
+
losses. The response has typed `classes` rows and an `excluded` list for unknown
|
|
61
|
+
roles or incomplete win/loss data. MCP exposes it through `get_player_stats`
|
|
62
|
+
with `category="classes"`. Hero switching means totals count participation,
|
|
63
|
+
not unique matches. Role IDs and the corrected Angela/Daredevil IDs were
|
|
64
|
+
verified against RivalsData's roster on 2026-09-30; Deadpool has separate role
|
|
65
|
+
variants (10571/10572/10573), while generic 1057 remains unclassified.
|
|
66
|
+
|
|
67
|
+
Camoufox inspection found no cumulative hero hours in current-season or
|
|
68
|
+
all-seasons player stats. Match hero usage does include `play_time` in seconds.
|
|
69
|
+
See `docs/API.md` for the recorded investigation; no playtime aggregation
|
|
70
|
+
method was added.
|
|
71
|
+
|
|
57
72
|
## Observed API details
|
|
58
73
|
|
|
59
74
|
The major UI areas inspected are home, player profile/tabs, global leaderboard,
|
|
@@ -103,7 +118,7 @@ Use a virtual environment. Install editable dependencies with
|
|
|
103
118
|
fallback work. The global host Python has unrelated package conflicts; don't
|
|
104
119
|
change global dependencies to resolve those.
|
|
105
120
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
Mocked tests in `tests/` cover hero lookups, typed responses, and calculated
|
|
122
|
+
class stats. Keep network-dependent checks opt-in. Run pytest, Ruff, and package
|
|
123
|
+
builds for code checks. Commit coherent
|
|
109
124
|
milestones and push to `origin` when explicitly requested by the project owner.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rivalsdata-api"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
description = "Python client and MCP server for public Marvel Rivals stats from RivalsData"
|
|
9
9
|
keywords = ["marvel-rivals", "rivalsdata", "game-stats", "api-client", "mcp"]
|
|
10
10
|
readme = "README.md"
|
|
@@ -7,10 +7,13 @@ from .exceptions import (
|
|
|
7
7
|
RivalsDataError,
|
|
8
8
|
RivalsDataHTTPError,
|
|
9
9
|
)
|
|
10
|
-
from .hero_ids import HERO_NAMES, hero_id, hero_name
|
|
10
|
+
from .hero_ids import HERO_CLASSES, HERO_NAMES, hero_class, hero_id, hero_name
|
|
11
11
|
from .models import (
|
|
12
12
|
BanRecord,
|
|
13
13
|
Character,
|
|
14
|
+
ClassModeStats,
|
|
15
|
+
ClassStatsRecord,
|
|
16
|
+
ClassStatsResponse,
|
|
14
17
|
CombatAverages,
|
|
15
18
|
CommBanHero,
|
|
16
19
|
CommBanInsights,
|
|
@@ -74,9 +77,13 @@ from .models import (
|
|
|
74
77
|
)
|
|
75
78
|
|
|
76
79
|
__all__ = [
|
|
80
|
+
"HERO_CLASSES",
|
|
77
81
|
"HERO_NAMES",
|
|
78
82
|
"BanRecord",
|
|
79
83
|
"Character",
|
|
84
|
+
"ClassModeStats",
|
|
85
|
+
"ClassStatsRecord",
|
|
86
|
+
"ClassStatsResponse",
|
|
80
87
|
"CloudflareError",
|
|
81
88
|
"CombatAverages",
|
|
82
89
|
"CommBanHero",
|
|
@@ -142,7 +149,8 @@ __all__ = [
|
|
|
142
149
|
"Top500Season",
|
|
143
150
|
"XPPage",
|
|
144
151
|
"XPRecord",
|
|
152
|
+
"hero_class",
|
|
145
153
|
"hero_id",
|
|
146
154
|
"hero_name",
|
|
147
155
|
]
|
|
148
|
-
__version__ = "1.
|
|
156
|
+
__version__ = "1.2.0"
|
|
@@ -46,9 +46,12 @@ HERO_NAMES: dict[str, str] = {
|
|
|
46
46
|
"1052": "Iron Fist",
|
|
47
47
|
"1053": "Emma Frost",
|
|
48
48
|
"1054": "Phoenix",
|
|
49
|
-
"1055": "
|
|
50
|
-
"1056": "
|
|
49
|
+
"1055": "Daredevil",
|
|
50
|
+
"1056": "Angela",
|
|
51
51
|
"1057": "Deadpool",
|
|
52
|
+
"10571": "Tankpool",
|
|
53
|
+
"10572": "DPSpool",
|
|
54
|
+
"10573": "Stratpool",
|
|
52
55
|
"1058": "Gambit",
|
|
53
56
|
"1059": "Elsa Bloodstone",
|
|
54
57
|
"1060": "White Fox",
|
|
@@ -61,6 +64,30 @@ HERO_NAMES: dict[str, str] = {
|
|
|
61
64
|
"1067": "Gorr the God Butcher",
|
|
62
65
|
}
|
|
63
66
|
|
|
67
|
+
# Observed on https://rivalsdata.com/stats on 2026-09-30. The unspecialized
|
|
68
|
+
# Deadpool ID (1057) has no single role and is deliberately left unresolved.
|
|
69
|
+
HERO_CLASSES: dict[str, str] = {
|
|
70
|
+
**dict.fromkeys((
|
|
71
|
+
"1011", "1018", "1022", "1027", "1035", "1037", "1039", "1042",
|
|
72
|
+
"1051", "1053", "1056", "1062", "1065", "1066", "10571",
|
|
73
|
+
), "tank"),
|
|
74
|
+
**dict.fromkeys((
|
|
75
|
+
"1016", "1020", "1023", "1025", "1028", "1031", "1046", "1047",
|
|
76
|
+
"1050", "1058", "1060", "1064", "10573",
|
|
77
|
+
), "support"),
|
|
78
|
+
**dict.fromkeys((
|
|
79
|
+
"1014", "1015", "1017", "1021", "1024", "1026", "1029", "1030",
|
|
80
|
+
"1032", "1033", "1034", "1036", "1038", "1040", "1041", "1043",
|
|
81
|
+
"1044", "1045", "1048", "1049", "1052", "1054", "1055", "1059",
|
|
82
|
+
"1061", "1063", "1067", "10572",
|
|
83
|
+
), "dps"),
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def hero_class(hero_id: object) -> str | None:
|
|
88
|
+
"""Return tank, support, or dps for a known role-specific hero ID."""
|
|
89
|
+
return HERO_CLASSES.get(str(hero_id))
|
|
90
|
+
|
|
64
91
|
|
|
65
92
|
def hero_name(hero_id: object) -> str | None:
|
|
66
93
|
"""Resolve a known playable hero ID, preserving unknown IDs for callers."""
|