Everything Andromeda serves to its public archive pages is available over a small, stable REST API. Public archive endpoints need no API key. Account collection automation uses scoped personal API tokens created from profile settings.
◇ Basics
All endpoints live under a single base URL. Every path in this document is relative to it.
https://classicchess.com
- Public methods — public archive endpoints are unauthenticated reads.
- Account authentication — account endpoints require
Authorization: Bearer <token>. - Responses — JSON (
application/json), newline-delimited JSON (application/x-ndjson), or PGN text (text/plain/application/x-chess-pgn). - Two surfaces — the master database (millions of historical games, 1840–2000) and the curated public archive (player and event memberships over shared game records).
- Spec — a compact OpenAPI document is available at
/api/openapi.yaml. - Be considerate — expensive public search and export endpoints are rate-limited. Keep request volume reasonable and set a descriptive
User-Agent.
◇ Conventions & pagination
List endpoints are paginated. Pass page and
page_size as query parameters. The default
page size is 50 and the maximum is
100 (larger values are clamped down).
Paginated payloads share a common envelope around their
results array:
{
"count": 128, // total matches found
"page": 1, // current page number
"page_size": 50, // items per page
"page_count": 3, // total number of pages
"results": [ ... ], // the page of items
"next": "https://classicchess.com/api/v1/games/?q=...&page=2&page_size=50",
"previous": null // null on the first page
}
The master game search returns at most
500 matching games per query (reported
as result_limit in the response); refine
your query q to narrow large result sets.
Search strings are capped at 120 characters and 8 searchable terms.
◇ Player resolver
/api/v1/players/
Resolve a name fragment against MasterDB player identities and curated public archives.
| Parameter | Required | Description |
|---|---|---|
q | yes | Name fragment, slug, or alias. |
limit | no | Maximum results per group (default 10, max 50). |
Example
curl "https://classicchess.com/api/v1/players/?q=tal"
◇ Errors
Errors use the appropriate HTTP status code and a consistent JSON body with a
machine-readable code and a human-readable
message:
{
"error": {
"code": "missing_query",
"message": "q is required."
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | missing_query | A required q parameter was empty. |
| 400 | missing_player | A required player parameter was empty. |
| 400 | missing_opponent | head_to_head mode needs an opponent. |
| 400 | invalid_mode | mode was not summary or head_to_head. |
| 400 | invalid_pagination | page or page_size was not a positive integer. |
| 404 | not_found | The requested game does not exist. |
| 503 | stats_unavailable | The master database is temporarily not connected. |
◇ Master database API
The master database holds millions of historical games (roughly 1840–2000). Use it to look up player statistics, search games, and pull individual game details or raw PGN. Games are addressed by an opaque token returned in every search result.
/api/v1/stats/
Player summary statistics, or a head-to-head record between two players.
| Parameter | Required | Description |
|---|---|---|
q | yes | Canonical stats query. Use one player name for a summary, or two names for head-to-head (e.g. Tal Smyslov). |
mode | legacy | summary (default) or head_to_head when using legacy params. |
player | legacy | Legacy player name to resolve (e.g. Capablanca). |
opponent | legacy head-to-head | Legacy second player; required when mode=head_to_head. |
Example
curl "https://classicchess.com/api/v1/stats/?q=Capablanca"
Response
{
"mode": "summary",
"query": { "q": "Capablanca", "player": "Capablanca", "opponent": "" },
"player": {
"query": "Capablanca",
"selected": {
"name": "Capablanca, Jose Raul",
"search_name": "capablanca jose raul",
"aliases": ["Capablanca", "Capablanca, J.R."],
"game_count": 1234,
"max_elo": 2725
},
"candidates": [ { "name": "...", "game_count": 1234, "max_elo": 2725 } ]
},
"opponent": null,
"result": {
"total": 1234, "wins": 600, "losses": 134, "draws": 500,
"as_white": 620, "as_black": 614
},
"opening_insights": { "...": "per-opening breakdown, or null" }
}
When a name is ambiguous, selected may be
null and candidates
lists the possible matches — refine the q
value to disambiguate. Legacy mode,
player, and
opponent params remain accepted. For
head_to_head,
opponent mirrors the
player block and
result is the record from
player's point of view.
/api/v1/games/
Search the master database for games. Returns a paginated list of game summaries.
| Parameter | Required | Description |
|---|---|---|
q | yes | Free-text query. Combine player names, an ECO code, and/or a year (e.g. Fischer Spassky 1972 or Kasparov B33). |
page | no | Page number (default 1). |
page_size | no | Items per page (default 50, max 100). |
Example
curl "https://classicchess.com/api/v1/games/?q=Fischer+Spassky+1972&page_size=10"
Response
{
"count": 21,
"page": 1,
"page_size": 10,
"page_count": 3,
"result_limit": 500,
"query": "Fischer Spassky 1972",
"results": [
{
"token": "a1b2c3d4",
"white": "Spassky, Boris V",
"black": "Fischer, Robert James",
"display_white": "Spassky, Boris V",
"display_black": "Fischer, Robert James",
"result": "1/2-1/2",
"event": "World Championship",
"site": "Reykjavik",
"date": "1972.07.11",
"played_on": "1972-07-11",
"year": 1972,
"round": "1",
"white_elo": 2660,
"black_elo": 2785,
"eco": "E56",
"opening": "Nimzo-Indian Defence",
"ply_count": 112,
"move_count": 56,
"source_count": 3,
"urls": {
"api_detail": "/api/v1/games/a1b2c3d4/",
"api_pgn": "/api/v1/games/a1b2c3d4/pgn/",
"gif": "/api/v1/games/a1b2c3d4/gif/",
"html": "/game-search/a1b2c3d4/"
}
}
],
"next": "https://classicchess.com/api/v1/games/?q=Fischer+Spassky+1972&page=2&page_size=10",
"previous": null
}/api/v1/games/export/
Bulk export MasterDB games as a PGN bundle or newline-delimited JSON.
| Parameter | Required | Description |
|---|---|---|
q | no | Export the current search result set (up to the API result limit). |
tokens | no | Comma-separated game tokens, max 300. POST may send the same list as plain text, up to 64 KiB. |
format | no | pgn (default) or ndjson. |
pgnInJson | no | Include PGN in NDJSON rows (default true). |
Example
curl "https://classicchess.com/api/v1/games/export/?q=Fischer+Spassky+1972" -o games.pgn curl "https://classicchess.com/api/v1/games/export/?q=Fischer+Spassky+1972&format=ndjson"
/api/v1/games/{token}/
Full detail for one master game, including its full PGN. The token comes from a search result.
Example
curl "https://classicchess.com/api/v1/games/a1b2c3d4/"
The body is the same object shown in search results, plus a
"pgn" field containing the full game text.
/api/v1/games/{token}/pgn/
The raw PGN for one master game as text/plain, ready to save or import elsewhere.
Example
curl "https://classicchess.com/api/v1/games/a1b2c3d4/pgn/" -o game.pgn
/api/v1/games/{token}/gif/
Registered-account animated GIF export for one master game. Add orientation=black to flip the board.
Example
curl -H "Authorization: Bearer $TOKEN" "https://classicchess.com/api/v1/games/a1b2c3d4/gif/" -o game.gif
◇ Study-archive API
The public archive is Andromeda's hand-curated collection of player and
event memberships over shared game records. Player-backed list/export
results retain a readable player_slug/game_slug
token for compatibility; the returned URLs always identify the shared
canonical game record.
/api/v1/public/players/
List the curated study players (those with at least one game).
| Parameter | Required | Description |
|---|---|---|
q | no | Filter players by name, slug, or source alias. |
Example
curl "https://classicchess.com/api/v1/public/players/?q=tal"
Response
{
"source": "public",
"query": "tal",
"count": 1,
"results": [
{
"name": "Mikhail Tal",
"slug": "mikhail-tal",
"game_count": 2431,
"criteria": "World Champion 1960-1961",
"urls": {
"api_games": "/api/v1/public/games/?archive_player=mikhail-tal",
"html_games": "/players/mikhail-tal/games/",
"html_about": "/players/mikhail-tal/about/"
}
}
]
}/api/v1/public/games/
Search player-backed public archive games. Returns a paginated list of game summaries whose navigable URLs target the shared canonical game record.
| Parameter | Required | Description |
|---|---|---|
q | no | Free-text query matched against players, event, site, date, ECO, and opening. |
archive_player | no | Restrict to one archive by player slug, name, or alias. |
since | no | Include games from this year or later. |
until | no | Include games from this year or earlier. |
sort | no | asc (default) or desc by game date. |
page | no | Page number (default 1). |
page_size | no | Items per page (default 50, max 100). |
Example
curl "https://classicchess.com/api/v1/public/games/?archive_player=mikhail-tal&since=1960&until=1965&sort=asc"
Response
{
"source": "public",
"query": "",
"archive_player": "mikhail-tal",
"since": 1960,
"until": 1965,
"sort": "asc",
"count": 42,
"page": 1,
"page_size": 50,
"page_count": 1,
"next": null,
"previous": null,
"results": [
{
"token": "mikhail-tal/tal-vs-larsen-1965",
"player": { "name": "Mikhail Tal", "slug": "mikhail-tal" },
"white": "Tal, Mikhail",
"black": "Larsen, Bent",
"display_white": "Tal, Mikhail",
"display_black": "Larsen, Bent",
"result": "1-0",
"result_key": "win",
"event": "Candidates",
"site": "Bled",
"date": "1965.07.20",
"played_on": "1965-07-20",
"round": "5",
"white_elo": null,
"black_elo": null,
"eco": "B33",
"opening": "Sicilian, Sveshnikov",
"move_count": 41,
"urls": {
"api_detail": "/api/v1/public/games/tal-vs-larsen-1965/",
"api_pgn": "/api/v1/public/games/tal-vs-larsen-1965/pgn/",
"gif": "/games/tal-vs-larsen-1965/gif/",
"html": "/games/tal-vs-larsen-1965/",
"pgn_download": "/games/tal-vs-larsen-1965/download/"
}
}
]
}/api/v1/public/games/export/
Bulk export player-backed public archive games as PGN or newline-delimited JSON. The player-scoped token form remains supported for existing export clients.
| Parameter | Required | Description |
|---|---|---|
q | no | Free-text archive search. |
archive_player | no | Restrict to one archive. |
since | no | Include games from this year or later. |
until | no | Include games from this year or earlier. |
tokens | no | Compatibility export selector: comma-separated player_slug/game_slug tokens, max 300 and up to 64 KiB. Use each result's canonical URL for game navigation. |
format | no | pgn (default) or ndjson. |
pgnInJson | no | Include PGN in NDJSON rows (default true). |
For POST, send the same compatibility token list as comma- or newline-separated text/plain.
Example
curl "https://classicchess.com/api/v1/public/games/export/?archive_player=mikhail-tal&since=1960&until=1965" -o tal-1960-1965.pgn
/api/v1/public/games/{game_slug}/
Canonical full detail for one public archive game. It works for player-backed and event-only games, returns the same shared Game identity, archive memberships, full PGN, and board-ready mainline.
Example
curl "https://classicchess.com/api/v1/public/games/tal-vs-larsen-1965/"
/api/v1/public/games/{game_slug}/pgn/
The canonical public PGN as text/plain.
Example
curl "https://classicchess.com/api/v1/public/games/tal-vs-larsen-1965/pgn/" -o tal.pgn
/api/v1/public/games/{player_slug}/{game_slug}/
Player-context compatibility detail, retained for existing clients. New integrations should use the canonical game-slug endpoint above.
Example
curl "https://classicchess.com/api/v1/public/games/mikhail-tal/tal-vs-larsen-1965/"
/api/v1/public/games/{player_slug}/{game_slug}/pgn/
Player-context compatibility PGN. New integrations should use the canonical game-slug endpoint above.
Example
curl "https://classicchess.com/api/v1/public/games/mikhail-tal/tal-vs-larsen-1965/pgn/" -o tal.pgn
◇ Annotated-book API
Public-domain annotated books are exposed as book metadata, paginated game lists, full annotated game detail, and all-games PGN exports.
/api/v1/annotated/books/
List annotated books and their source metadata.
/api/v1/annotated/books/{book_slug}/games/
Paginated games for one annotated book.
/api/v1/annotated/books/{book_slug}/games/{game_slug}/
One annotated game with PGN and public-domain annotation notes.
/api/v1/annotated/books/{book_slug}/pgn/
All games in one annotated book as a PGN bundle.
◇ Opening explorer API
/api/v1/opening-explorer/
Explore a position from an indexed Andromeda source. Supports Lichess-style fen + play lookup.
| Parameter | Required | Description |
|---|---|---|
fen | no | Root FEN; omitted means the normal starting position. |
play | no | Comma-separated UCI moves from the root, such as d2d4,d7d5; max 100 moves / 512 characters. |
moves | no | Number of candidate moves (default 12, max 50). |
topGames | no | Number of reference games (default site setting, max 15). |
since / until | no | Inclusive year range for sources with year indexes (1400-9999). |
source_type / source_key | no | Select a public key returned by the sources endpoint; the master database key is masterdb. |
Example
curl "https://classicchess.com/api/v1/opening-explorer/?play=d2d4,d7d5&moves=8&topGames=15&until=1900"
/api/v1/opening-explorer/sources/
List currently indexed opening explorer sources.
◇ Mobile app support
/api/v1/mobile/home/
Native-app home payload: front-door counts, Game of the Day, and primary API links.
/api/v1/mobile/android/release/
Native Android release metadata, including version code, APK URL, size, and SHA-256 checksum for in-app update verification.
◇ Account API
Account endpoints accept bearer credentials. Automation should use personal API tokens created from profile settings; native mobile apps can use the mobile login endpoint to exchange username/password plus TOTP, if enabled, for a mobile session credential. All credentials are stored hashed server-side, revocable, and limited by explicit scopes.
Authorization: Bearer andm_pat_... Authorization: Bearer andm_mobile_...
| Scope | Allows |
|---|---|
account:read | Read the authenticated account summary. |
library:read | List account game collections. |
library:write | Create collections and import public archive games into them. |
/api/v1/account/mobile/login/
Native-app username/password sign-in. If the account has authenticator 2FA enabled, retry with two_factor_code after receiving two_factor_required.
Example
curl -X POST "https://classicchess.com/api/v1/account/mobile/login/" \
-H "Content-Type: application/json" \
-d '{"username":"YOUR_USERNAME","password":"YOUR_PASSWORD","device_name":"Android phone"}'/api/v1/account/mobile/logout/
Revoke the current mobile session credential.
/api/v1/account/mobile/settings/
Native-app account settings for board theme, piece set, and Stockfish preferences. Requires a mobile session credential.
/api/v1/account/me/
Authenticated account summary. Requires account:read.
/api/v1/account/collections/
List collections with library:read, or create one with library:write.
Create example
curl -X POST "https://classicchess.com/api/v1/account/collections/" \
-H "Authorization: Bearer $ANDROMEDA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Tal 1960-1965"}'/api/v1/account/collections/{id}/
List games inside one owned collection with library:read; rename or delete it with library:write. Other users' collections return 404.
/api/v1/account/collections/{id}/items/{item_id}/
Remove one game from an owned collection. Requires library:write; other users' collections return 404.
/api/v1/account/collections/import/
Create or reuse a collection and import public archive player games. Requires library:write.
Example
curl -X POST "https://classicchess.com/api/v1/account/collections/import/" \
-H "Authorization: Bearer $ANDROMEDA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"source":"public_player_games","name":"Tal 1960-1965","archive_player":"mikhail-tal","since":1960,"until":1965}'Response
{
"ok": true,
"collection": { "id": 123, "name": "Tal 1960-1965", "sort": "added", "game_count": 42 },
"created_collection": true,
"matched": 42,
"added": 42,
"already_present": 0,
"skipped": 0
}/api/v1/account/imported-games/
List/search imported games with library:read, or import one pasted PGN / supported public game URL with library:write.
/api/v1/account/imported-games/{slug}/
Owned imported-game detail with PGN and board-ready mainline. Other users' private imports return 404.
◇ Command-line client
If you'd rather not assemble URLs by hand, there is a small,
dependency-free Python client. The public download,
pull_api.py, is a standalone script that
wraps every endpoint above. It needs only Python 3 (no third-party
packages) and prints JSON — or PGN with
--pgn — to stdout.
In the repository, the reusable client lives in the
andromeda_api package. The root
pull_api.py script is just a compatibility
wrapper around that library, so Python code can import
AndromedaClient directly.
↧ Download pull_api.py
or grab it from the shell:
curl -O https://classicchess.com/api/pull_api.py
Then run it
# Master database
python3 pull_api.py master stats -q Capablanca
python3 pull_api.py master games --players Fischer Spassky --year 1972
python3 pull_api.py master export --players Fischer Spassky --year 1972 --output games.pgn
python3 pull_api.py master pgn a1b2c3d4 --output game.pgn
python3 pull_api.py players tal
# Study archive
python3 pull_api.py public players --plain
python3 pull_api.py public games --archive-player mikhail-tal --eco B33 --all-pages
python3 pull_api.py public games --archive-player mikhail-tal --since 1960 --until 1965
python3 pull_api.py public export --archive-player mikhail-tal --output tal.pgn
python3 pull_api.py public pgn mikhail-tal/tal-vs-larsen-1965
# Account collections
ANDROMEDA_API_TOKEN=... python3 pull_api.py account collections list
ANDROMEDA_API_TOKEN=... python3 pull_api.py account collections import-player "Tal 1960-1965" mikhail-tal --since 1960 --until 1965Library usage inside the repository
python3 -m andromeda_api players tal
from andromeda_api import AndromedaClient
client = AndromedaClient()
stats = client.master_stats(query="Fischer Spassky")
games = client.master_games("Fischer Spassky 1972", page_size=10)
Add --base-url to point the client at a
different host, --all-pages to follow
pagination automatically, and --limit to cap
the number of results. Account commands read
ANDROMEDA_API_TOKEN or
--api-token.