API Reference

The API is built for importing a user's games into another system. It gives you lightweight JSON lists, full game details, PGN export, and an incremental sync feed. Every response is limited to the user behind the access token.

Base URL: https://api.chessscanner.com

Authenticate every request with the bearer token from authorization:

Authorization: Bearer ACCESS_TOKEN

Get the current user

GET /api/v1/me (scope user.read)

Returns the authenticated user's basic profile.

curl https://api.chessscanner.com/api/v1/me \
  -H "Authorization: Bearer $TOKEN"
{
  "data": {
    "id": "5f8c…",
    "name": "Magnus",
    "email": "magnus@example.com",
    "image": "https://cdn.chessscanner.com/…"
  }
}

The id is a pairwise pseudonymous identifier. It's stable for your app but different for every app, and it's never the user's real internal id. Use it as the user key in your system.

List databases

GET /api/v1/databases (scope databases.read)

Returns the databases owned by the user.

curl https://api.chessscanner.com/api/v1/databases \
  -H "Authorization: Bearer $TOKEN"
{
  "data": [
    {
      "id": "9a1c…",
      "name": "My Games",
      "slug": "my-games",
      "private": true,
      "maxGames": 500,
      "createdAt": "2026-06-14T10:00:00.000Z",
      "updatedAt": "2026-06-14T10:00:00.000Z"
    }
  ]
}

List games in a database

GET /api/v1/databases/{id}/games (scope games.read)

Returns the games in one database. Add format=pgn to download every game as a single PGN file instead of JSON.

Parameters

  • Name
    id
    Type
    string
    Required
    Description

    Database UUID or slug, in the path.

  • Name
    limit
    Type
    integer
    OptionalDefault: 50
    Description

    Number of games per page (max 200).

  • Name
    offset
    Type
    integer
    OptionalDefault: 0
    Description

    How many games to skip.

  • Name
    format
    Type
    string
    Optional
    Description

    Set to pgn for a bulk PGN export.

# JSON
curl "https://api.chessscanner.com/api/v1/databases/<id>/games?limit=25" \
  -H "Authorization: Bearer $TOKEN"

# PGN
curl "https://api.chessscanner.com/api/v1/databases/<id>/games?format=pgn" \
  -H "Authorization: Bearer $TOKEN"

List games

GET /api/v1/games (scope games.read)

Returns the user's games across all databases as a lightweight list (no moves or PGN). For a single database, use GET /api/v1/databases/{id}/games.

Parameters

  • Name
    limit
    Type
    integer
    OptionalDefault: 50
    Description

    Number of games per page (max 200).

  • Name
    offset
    Type
    integer
    OptionalDefault: 0
    Description

    How many games to skip.

{
  "data": [
    {
      "id": "1b2d…",
      "slug": "magnus-vs-hikaru",
      "databaseId": "9a1c…",
      "event": "Casual",
      "date": "2026-05-01",
      "result": "1-0",
      "eco": "B90",
      "white": {
        "name": "Magnus",
        "elo": 2830,
        "fideId": "1503014",
        "country": "NOR"
      },
      "black": {
        "name": "Hikaru",
        "elo": 2790,
        "fideId": "2016192",
        "country": "USA"
      }
    }
  ]
}

The white/black player refs include fideId and country in every game list, the changes feed, and the game detail — use the FIDE ID to match players against your own records.

Get a game

GET /api/v1/games/{id} (scope games.read)

Returns one game with full data, including the moves, PGN, and metadata — the game's extra PGN tag pairs (e.g. a Site tag) as a JSON object. Add format=pgn (or send Accept: application/x-chess-pgn) to get the PGN directly; the PGN exports include those extra tags as well.

Parameters

  • Name
    id
    Type
    string
    Required
    Description

    Game UUID, in the path.

  • Name
    format
    Type
    string
    Optional
    Description

    Set to pgn to return the PGN instead of JSON.

curl "https://api.chessscanner.com/api/v1/games/<id>?format=pgn" \
  -H "Authorization: Bearer $TOKEN"
{
  "data": {
    "id": "1b2d…",
    "result": "1-0",
    "white": { "name": "Magnus", "fideId": "1503014", "country": "NOR" },
    "black": { "name": "Hikaru", "fideId": "2016192", "country": "USA" },
    "fen": "…",
    "moves": "e2e4 c7c5 …",
    "pgn": "1. e4 c5 …",
    "metadata": { "Site": "Oslo" }
  }
}

Changes feed

GET /api/v1/changes (scope games.read)

The recommended way to keep an external system up to date. It returns games ordered by when they last changed, including deleted ones (with deleted: true), and pages through them with an opaque cursor.

Parameters

  • Name
    cursor
    Type
    string
    Optional
    Description

    Opaque cursor from a previous response. Omit it on the first run.

  • Name
    since
    Type
    string
    Optional
    Description

    ISO timestamp to start from on the first run instead of a full backfill.

  • Name
    limit
    Type
    integer
    OptionalDefault: 100
    Description

    Number of changes per page (max 500).

# First run
curl "https://api.chessscanner.com/api/v1/changes?limit=200" \
  -H "Authorization: Bearer $TOKEN"

# Later runs: pass the previous nextCursor
curl "https://api.chessscanner.com/api/v1/changes?cursor=<opaque>" \
  -H "Authorization: Bearer $TOKEN"
{
  "data": [
    {
      "id": "1b2d…",
      "databaseId": "9a1c…",
      "updatedAt": "2026-06-14T10:00:00.000Z",
      "deleted": false,
      "white": { "name": "Magnus" },
      "black": { "name": "Hikaru" },
      "result": "1-0"
    }
  ],
  "nextCursor": "<opaque>",
  "hasMore": true
}

Rate limiting

Requests are rate limited per app and user. If you go over the limit you get 429 Too Many Requests with a Retry-After header and X-RateLimit-* headers. Back off and retry after the time it indicates.

Errors

StatusMeaning
401 invalid_tokenMissing, invalid, or expired access token. Re-authorize.
403 insufficient_scopeThe token lacks the required scope
404 not_foundResource not found, or not owned by the user
429 rate_limitedToo many requests. See Retry-After.

Errors use the shape { "error": "<code>", "error_description": "<message>" }.

Was this page helpful?