A Python command-line tool that retrieves and prints the chess rating of a person from the specified ratings platform.
Clone this repository and change directory into its root, then create a Python virtual environment in which to run it:
git clone git@github.com:philhanna/chess-rating.git
cd chess-rating
python -m venv .venv
source .venv/bin/activateCopy the sample configuration sample_config.yaml into the configuration
directory ~/.config/chess-rating as config.yaml
Install the package and its runtime dependencies with:
python -m pip install -e .note the "." after the -e option!
Make a link to the command line interface into a directory in your path:
ln .venv/bin/rating path/to/bin/ratingAfter installation, the CLI is available as:
rating --helpUse the installed console script:
rating -u some_uscf_id
rating -l some_lichess_user
rating -c some_chesscom_user
rating -f some_fide_idYou can still run the module directly with python -m rating, but the packaged command is the preferred entry point. The main CLI implementation lives in rating.application.rating, while rating.__main__ remains a thin compatibility wrapper.
usage: rating [-h] [-v] [-j] (-u | -l | -c | -f) [player]
Fetches and prints a players's chess rating from USCF, FIDE, Lichess, or Chess.com.
positional arguments:
player The player's ID or name.
options:
-h, --help show this help message and exit
-v, --version show the project version and exit
-j, --json Create JSON output
-u, --uscf Use USCF platform
-l, --lichess Use Lichess platform
-c, --chess Use chess.com platform
-f, --fide Use FIDE platform
One source flag is required for rating lookups. For example, use rating --uscf 12910923
rather than relying on an implicit default source.
Data from chess.com is obtained using this URL:
https://api.chess.com/pub/player/{userid}/stats
A get request for that URL returns JSON that looks like this:
{
"chess_daily": {
"last": {
"rating": 1018,
"date": 1741387755,
"rd": 82
},
"best": {
"rating": 1200,
"date": 1715625334,
"game": "https://www.chess.com/game/daily/708913025"
},
"record": {
"win": 19,
"loss": 15,
"draw": 1,
"time_per_move": 9703,
"timeout_percent": 0
}
},
"chess_rapid": {
"last": {
"rating": 970,
"date": 1740903985,
"rd": 43
},
"best": {
"rating": 1108,
"date": 1733977950,
"game": "https://www.chess.com/game/live/109817472659"
},
"record": {
"win": 458,
"loss": 432,
"draw": 41
}
},
...
}The application reads the chess_* categories from this response and
normalizes them into the shared rating profile schema. For example:
chess_rapid.last.rating->ratings.rapidchess_blitz.last.rating->ratings.blitzchess_bullet.last.rating->ratings.bulletchess_daily.last.rating->ratings.correspondence
Data from Lichess is obtained using this URL:
https://lichess.org/api/user/{userid}
A get request for that URL returns JSON that includes a perfs object like
this:
{
"id": "pehanna",
"username": "pehanna",
"perfs": {
"bullet": {
"games": 5,
"rating": 1500
},
"blitz": {
"games": 10,
"rating": 1600
},
"rapid": {
"games": 0,
"rating": 1400
},
"classical": {
"games": 7,
"rating": 1700
},
"puzzle": {
"games": 3,
"rating": 2100
}
}
}The application keeps only categories with at least one game played and normalizes them like this:
classical->ratings.standardrapid->ratings.rapidblitz->ratings.blitzbullet->ratings.bullet- provider-specific categories such as
puzzleorultraBullet->extras
Data from US Chess is obtained using this URL:
https://ratings-api.uschess.org/api/v1/members/{userid}/sections
A get request for that URL returns JSON containing a list of section results for the player, such as:
{
"items": [
{
"sectionNumber": 1,
"sectionName": "March U1600",
"startDate": "2026-03-04",
"endDate": "2026-03-25",
"ratingSystem": "R",
"ratingRecords": [
{
"preRating": 1227,
"postRating": 1260,
"ratingSource": "R"
}
],
"event": {
"id": "202603250203",
"name": "Triangle Chess Adults Marathon March",
"stateCode": "NC"
}
}
]
}The application currently uses the newest section entry only. From that first item, it extracts:
items[0].ratingRecords[0].postRating->ratings.standarditems[0].endDate->metadata.as_of
For more detail on this payload shape, see docs/uscf_sections.md.
Data from FIDE is obtained using this URL:
https://ratings.fide.com/profile/{userid}
FIDE does not provide the needed data as a simple JSON API for this use case, so the application reads the public HTML profile page and extracts the visible rating cards. The relevant part of the page looks roughly like this:
<div class="profile-container">
<h1 class="player-title">Magnus Carlsen</h1>
</div>
<div class="profile-section">
<div class="profile-games">
<div>
<p>2835</p>
<p>Standard</p>
</div>
<div>
<p>2810</p>
<p>Rapid</p>
</div>
<div>
<p>2885</p>
<p>Blitz</p>
</div>
</div>
</div>The application scrapes the player name and rating categories from this HTML and normalizes them like this:
Standard->ratings.standardRapid->ratings.rapidBlitz->ratings.blitz- values such as
Not rated->nullin JSON output /Not ratedin plain text - any additional FIDE categories ->
extras