{"openapi":"3.1.0","info":{"title":"ChessXP FFE Player API","description":"**ChessXP FFE Player API** — read-only access to the French chess\nplayer roster.\n\n### What you get\n\nFor each player, the public payload exposes (default, post-trim):\n``id``, ``ffe_id``, ``last_name``, ``first_name``, ``gender``,\n``ffe_licence_number``, ``ffe_licence`` (Papi type: 1=N expired, 2=A, 3=B),\n``licence_type`` (``\"A\"`` / ``\"B\"`` / null), ``federation``,\n``league``, ``club``, ``fide_id``, ``fide_title``, ``birth_year``,\n``category``, ``is_active_this_season``, the three rating buckets\n(``standard_rating``, ``rapid_rating``, ``blitz_rating``) and their\n``*_type`` / ``*_label`` flags, ``games_*`` (when available), and a\n``source`` block with the upstream deep link plus the ingestion\ntimestamp.\n\n``date_of_birth`` and ``city`` are excluded in the default deployment\nto minimise personal data. Dataset provenance, provider name, and\nupstream dataset URL are documented once in ``LICENCE.md`` (supplied\nwith your API access materials) — they are not repeated in OpenAPI or\nper-response metadata.\n\n### Optional FIDE blocks (``?include=``)\n\nAppend ``?include=<tokens>`` (comma-separated) to any single-player\nor batch / search endpoint to attach additional data sourced from FIDE\nmonthly rating lists:\n\n| Token | What you get |\n|---|---|\n| ``fide`` | FIDE profile — title, federation, current ratings, photo, sex, women's title, K-factor |\n| ``fide_ratings`` | Full monthly rating history (standard / rapid / blitz) with game counts |\n| ``fide_stats`` | Season-to-date stats — peak, YTD delta, last-published delta, games YTD, first year |\n\n> **Note:** FIDE blocks require the player to have a ``fide_id`` and a\n> cached FIDE entry (populated by the weekly refresh pipeline).\n> Players without a FIDE ID or with no cache entry silently omit the\n> requested blocks rather than returning an error.\n\n### Authentication\n\nEvery protected endpoint expects an ``X-API-Key`` header. Click\n**Authorize** in the top-right and paste your key. Need one? Reach\nout via the lead form on\n[api-ffe.chessxp.com](https://api-ffe.chessxp.com).\n\n### Rate limits\n\nExternal keys: **120 requests / minute** (sliding window). When you\nhit the limit you receive a ``429`` with a ``Retry-After`` header.\nChessXP internal keys bypass this limit.\n\n### Refresh cadence\n\nThe dataset refreshes weekly (Monday 03:00 Europe/Paris). The public\n``/health`` probe publishes ``row_count``, ``last_refresh_utc``, and\n``stale_days_since_refresh`` so you can verify currency without an API\nkey. ChessXP operators also use an authenticated verbose health route\nthat is intentionally omitted from this schema.\n\n### Quick start\n\n> L5: the licence ``X00000`` below is a **synthetic placeholder** —\n> replace it with a real FFE licence number when calling the API.\n\n```bash\ncurl -fsS \\\n  -H \"X-API-Key: <your-key>\" \\\n  https://ffe.chessxp.com/api/player/X00000\n```\n\n```javascript\nconst r = await fetch(\n  \"https://ffe.chessxp.com/api/player/X00000\",\n  { headers: { \"X-API-Key\": process.env.FFE_KEY } },\n);\nconst player = await r.json();\n```\n\n```python\nimport requests\nr = requests.get(\n    \"https://ffe.chessxp.com/api/player/X00000\",\n    headers={\"X-API-Key\": API_KEY},\n    timeout=10,\n)\nr.raise_for_status()\nprint(r.json())\n```","version":"1.0.0"},"servers":[{"url":"https://ffe.chessxp.com","description":"Production"}],"paths":{"/health":{"get":{"tags":["health"],"summary":"Liveness / readiness probe","description":"Returns overall service status and database health. No API key required. ``status`` is ``healthy`` when the DB is reachable and was refreshed within the stale threshold, ``degraded`` when the DB is reachable but stale, or ``unhealthy`` when the DB cannot be opened (HTTP 503). ``database.row_count`` and ``database.last_refresh_utc`` let you verify that the weekly dataset refresh ran successfully. ``database.stale_days_since_refresh`` counts calendar days since the last refresh (null when no refresh timestamp is recorded).","operationId":"public_health_check_health_get","responses":{"200":{"description":"JSON object: ``{status, database: {exists, accessible, row_count, last_refresh_utc, stale_days_since_refresh}}``","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"tags":["root"],"summary":"Service identity","description":"Returns the service name, current API version, a short description, and the URL of this documentation page. No API key required. Useful as a quick connectivity check or for agents that auto-discover the API surface.","operationId":"root__get","responses":{"200":{"description":"JSON object: ``{name, version, description, docs_url}``","content":{"application/json":{"schema":{}}}}}}},"/api/players/top":{"get":{"tags":["players"],"summary":"Top players by FIDE rating delta","description":"Ranks FFE-licensed players with a joined ``fide_lichess_cache`` row. ``metric=season_gain`` uses persisted season-to-date deltas; ``metric=last_published_delta`` uses the delta between the last two FIDE publication points. Only players with an active FFE licence type (Papi A/B, ``ffe_licence`` 2 or 3) are included; expired N rows are excluded at refresh and from materialized rankings.","operationId":"top_players_api_players_top_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"metric","in":"query","required":true,"schema":{"type":"string","pattern":"^(season_gain|last_published_delta)$","description":"``season_gain`` (YTD vs season anchor) or ``last_published_delta`` (last FIDE list step).","title":"Metric"},"description":"``season_gain`` (YTD vs season anchor) or ``last_published_delta`` (last FIDE list step)."},{"name":"discipline","in":"query","required":false,"schema":{"type":"string","pattern":"^(standard|rapid|blitz)$","description":"Rating pool used for ordering and tie-break.","default":"standard","title":"Discipline"},"description":"Rating pool used for ordering and tie-break."},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4,"pattern":"^[A-Za-z]{2,4}$"},{"type":"null"}],"title":"League"}},{"name":"federation","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3,"pattern":"^[A-Za-z]{3}$"},{"type":"null"}],"title":"Federation"}},{"name":"club_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":80,"pattern":"^[a-z0-9-]{1,80}$"},{"type":"null"}],"title":"Club Slug"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Category"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Players sorted by the requested delta (descending).","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPublic"},"title":"Response Top Players Api Players Top Get"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/batch":{"get":{"tags":["players"],"summary":"Batch lookup by FFE licence numbers","operationId":"get_players_batch_api_player_batch_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"licences","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":1050,"description":"Comma-separated FFE licences (1..50, alphanumeric)","examples":["W53002,B12345"],"title":"Licences"},"description":"Comma-separated FFE licences (1..50, alphanumeric)"},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPublic"},"title":"Response Get Players Batch Api Player Batch Get"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/search-by-name":{"get":{"tags":["players"],"summary":"Search players by last + first name","description":"Accent-insensitive and case-insensitive partial match on last name (required) and optional first name.","operationId":"search_players_by_last_and_first_name_api_player_search_by_name_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"last_name","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":80,"description":"Last name (required, 2–80 chars)","examples":["BRAVO"],"title":"Last Name"},"description":"Last name (required, 2–80 chars)"},{"name":"first_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":80},{"type":"null"}],"description":"First name (optional, 2–80 chars)","examples":["Jorge"],"title":"First Name"},"description":"First name (optional, 2–80 chars)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Max results. Use 10 for autocomplete.","default":25,"title":"Limit"},"description":"Max results. Use 10 for autocomplete."},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4,"pattern":"^[A-Za-z]{2,4}$"},{"type":"null"}],"title":"League"}},{"name":"federation","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3,"pattern":"^[A-Za-z]{3}$"},{"type":"null"}],"title":"Federation"}},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4000,"minimum":0},{"type":"null"}],"title":"Min Rating"}},{"name":"max_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4000,"minimum":0},{"type":"null"}],"title":"Max Rating"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Category"}},{"name":"match","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(contains)?$"},{"type":"null"}],"description":"Match mode: omit (default) = prefix search (fastest); 'contains' = substring search via FTS5.","title":"Match"},"description":"Match mode: omit (default) = prefix search (fastest); 'contains' = substring search via FTS5."},{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Include inactive (N-licence) players in results.","default":false,"title":"Include Inactive"},"description":"Include inactive (N-licence) players in results."},{"name":"include_unrated","in":"query","required":false,"schema":{"type":"boolean","description":"Include players with no FFE or FIDE rating.","default":false,"title":"Include Unrated"},"description":"Include players with no FFE or FIDE rating."},{"name":"fide_limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":0,"description":"Max players that receive a live FIDE enrichment block. Ignored when ?include=fide is absent. Recommend ≤10 for autocomplete.","default":20,"title":"Fide Limit"},"description":"Max players that receive a live FIDE enrichment block. Ignored when ?include=fide is absent. Recommend ≤10 for autocomplete."},{"name":"cross","in":"query","required":false,"schema":{"type":"boolean","description":"When true, union FFE results with FIDE-only players from ``fide_player_index`` (players with no FFE licence).  Each result includes a ``source`` field: ``'ffe'`` or ``'fide'``.","default":false,"title":"Cross"},"description":"When true, union FFE results with FIDE-only players from ``fide_player_index`` (players with no FFE licence).  Each result includes a ``source`` field: ``'ffe'`` or ``'fide'``."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Matched players (post-trim, source attached)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPublic"},"title":"Response Search Players By Last And First Name Api Player Search By Name Get"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/search-by-club":{"get":{"tags":["players"],"summary":"Search players by club name","description":"Accent-insensitive and case-insensitive partial match on club.","operationId":"search_players_by_club_api_player_search_by_club_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"club","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":120,"description":"Club name (required, 2–120 chars)","examples":["Échiquier Rennais"],"title":"Club"},"description":"Club name (required, 2–120 chars)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":25,"title":"Limit"}},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4,"pattern":"^[A-Za-z]{2,4}$"},{"type":"null"}],"title":"League"}},{"name":"federation","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3,"pattern":"^[A-Za-z]{3}$"},{"type":"null"}],"title":"Federation"}},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4000,"minimum":0},{"type":"null"}],"title":"Min Rating"}},{"name":"max_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4000,"minimum":0},{"type":"null"}],"title":"Max Rating"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Category"}},{"name":"match","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(contains)?$"},{"type":"null"}],"description":"Match mode: omit = prefix; 'contains' = FTS5 substring.","title":"Match"},"description":"Match mode: omit = prefix; 'contains' = FTS5 substring."},{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Inactive"}},{"name":"include_unrated","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Unrated"}},{"name":"fide_limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":0,"default":20,"title":"Fide Limit"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Matched players (post-trim, source attached)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPublic"},"title":"Response Search Players By Club Api Player Search By Club Get"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/search":{"get":{"tags":["players"],"summary":"Search players by name (legacy)","description":"Legacy endpoint kept for backwards compatibility. Prefer `/player/search-by-name` (separate last_name + first_name fields, accent-insensitive).","operationId":"search_players_legacy_api_player_search_get","deprecated":true,"security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":80,"description":"Name to search for (2–80 chars)","examples":["BRAVO"],"title":"Name"},"description":"Name to search for (2–80 chars)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":10,"title":"Limit"}},{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4,"pattern":"^[A-Za-z]{2,4}$"},{"type":"null"}],"title":"League"}},{"name":"federation","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3,"pattern":"^[A-Za-z]{3}$"},{"type":"null"}],"title":"Federation"}},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4000,"minimum":0},{"type":"null"}],"title":"Min Rating"}},{"name":"max_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4000,"minimum":0},{"type":"null"}],"title":"Max Rating"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Category"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Matched players (post-trim, source attached)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPublic"},"title":"Response Search Players Legacy Api Player Search Get"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/fide/search-by-name":{"get":{"tags":["fide"],"summary":"Search FIDE players by name (cross-base)","description":"Accent-insensitive prefix search over the full FIDE rating list, including players with no FFE licence.  FTS5-backed for sub-50 ms latency across ~1 M FIDE entries.","operationId":"search_fide_players_by_name_api_fide_search_by_name_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"last_name","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":80,"description":"Last name (required, 2–80 chars).","examples":["VIGNELLES"],"title":"Last Name"},"description":"Last name (required, 2–80 chars)."},{"name":"first_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":80},{"type":"null"}],"description":"First name (optional, 2–80 chars).","title":"First Name"},"description":"First name (optional, 2–80 chars)."},{"name":"federation","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3,"pattern":"^[A-Za-z]{3}$"},{"type":"null"}],"description":"3-letter FIDE federation code (e.g. 'FRA').","title":"Federation"},"description":"3-letter FIDE federation code (e.g. 'FRA')."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results to return.","default":25,"title":"Limit"},"description":"Max results to return."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FidePlayerPublic"},"title":"Response Search Fide Players By Name Api Fide Search By Name Get"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type)."},"503":{"description":"Database unavailable"}}}},"/api/league/list":{"get":{"tags":["leagues"],"summary":"List FFE regional leagues","description":"Returns every distinct ``league`` value on the player roster with player and distinct-club counts. ``code`` matches the ``league`` query parameter on search and top-player routes.","operationId":"leagues_list_api_league_list_get","responses":{"200":{"description":"League summaries sorted by name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeagueListResponse"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ChessXPApiKey":[]}]}},"/api/club/list":{"get":{"tags":["clubs"],"summary":"List FFE clubs","description":"Returns a paginated list of FFE clubs with name, slug, player count, league, and federation. Use ``league`` or ``federation`` to narrow results. The ``slug`` field can be passed to ``GET /api/club/{slug}/players`` to fetch that club's roster.","operationId":"clubs_list_api_club_list_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"league","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4,"pattern":"^[A-Za-z]{2,4}$"},{"type":"null"}],"description":"Filter by regional league code (2–4 letters, e.g. IDF, BRE, NAQ).","title":"League"},"description":"Filter by regional league code (2–4 letters, e.g. IDF, BRE, NAQ)."},{"name":"federation","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3,"pattern":"^[A-Za-z]{3}$"},{"type":"null"}],"description":"Filter by federation code (3 letters, e.g. FRA).","title":"Federation"},"description":"Filter by federation code (3 letters, e.g. FRA)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Maximum number of clubs to return (1–500, default 100).","default":100,"title":"Limit"},"description":"Maximum number of clubs to return (1–500, default 100)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of clubs to skip for pagination (default 0).","default":0,"title":"Offset"},"description":"Number of clubs to skip for pagination (default 0)."}],"responses":{"200":{"description":"Paginated club list with total count and source block.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClubListResponse"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/club/{slug}/players":{"get":{"tags":["clubs"],"summary":"Players in a club (by slug)","description":"Returns up to 200 players belonging to the club identified by ``slug`` (obtain slugs from ``GET /api/club/list``). Pass ``?include=fide``, ``fide_ratings``, or ``fide_stats`` to attach FIDE data to each player (same semantics as single-player lookup).","operationId":"club_players_api_club__slug__players_get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{1,80}$","description":"URL-safe club slug (lowercase letters, digits, hyphens). Obtain slugs from ``GET /api/club/list``.","examples":["us-orleans-echecs"],"title":"Slug"},"description":"URL-safe club slug (lowercase letters, digits, hyphens). Obtain slugs from ``GET /api/club/list``."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"List of players in the club (post-trim, source attached).","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPublic"},"title":"Response Club Players Api Club  Slug  Players Get"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/id/{player_id}":{"get":{"tags":["players"],"summary":"Get player by internal ID","description":"Look up a single player by the API's internal integer ID. Add ``?include=fide``, ``fide_ratings``, or ``fide_stats`` to attach optional FIDE blocks to the response.","operationId":"get_player_by_id_api_player_id__player_id__get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"player_id","in":"path","required":true,"schema":{"type":"integer","maximum":10000000,"minimum":1,"title":"Player Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Single player (post-trim, source attached)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerPublic"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Player not found (or opted out).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/fide/{fide_id}":{"get":{"tags":["players"],"summary":"Get player by FIDE ID","description":"Look up a single FFE-licensed player by their FIDE ID (e.g. 3418057). Returns 404 when no player in the FFE roster has that FIDE ID. Add ``?include=fide``, ``fide_ratings``, or ``fide_stats`` to attach optional FIDE blocks.","operationId":"get_player_by_fide_id_api_player_fide__fide_id__get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"fide_id","in":"path","required":true,"schema":{"type":"integer","maximum":99999999,"minimum":1,"title":"Fide Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Single player (post-trim, source attached)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerPublic"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Player not found (or opted out).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/player/{licence_number}":{"get":{"tags":["players"],"summary":"Get player by FFE licence number","description":"Look up a single player by their FFE licence number (e.g. W53002). Case-insensitive. Returns 404 for unknown or opted-out licences. Add ``?include=fide``, ``fide_ratings``, or ``fide_stats`` to attach optional FIDE blocks to the response.","operationId":"get_player_by_licence_api_player__licence_number__get","security":[{"ChessXPApiKey":[]}],"parameters":[{"name":"licence_number","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":20,"pattern":"^[A-Za-z0-9]{1,20}$","description":"FFE licence number (1–20 alphanumeric chars)","examples":["X00000"],"title":"Licence Number"},"description":"FFE licence number (1–20 alphanumeric chars)"},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`","examples":["fide","fide,fide_ratings","fide_stats"],"title":"Include"},"description":"Comma-separated optional blocks to attach to the player payload. Allowed tokens:\n\n- **`fide`** — FIDE profile: title, federation, current ratings, photo, sex, women's title, K-factor.\n- **`fide_ratings`** — Full FIDE rating history per discipline (standard / rapid / blitz), one point per published monthly list, with game count where available.\n- **`fide_stats`** — Season-to-date statistics: peak rating, year-to-date delta, last-published-month delta, games played YTD, and first year rated — per discipline.\n\nExample: `include=fide,fide_stats`"}],"responses":{"200":{"description":"Single player (post-trim, source attached)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerPublic"}}}},"403":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request parameters failed validation (length / pattern / type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Player not found (or opted out).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"ClubListResponse":{"properties":{"clubs":{"items":{"$ref":"#/components/schemas/ClubSummary"},"type":"array","title":"Clubs","description":"Matched clubs (up to ``limit`` entries)."},"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Total number of clubs matching the applied filters (before limit/offset)."},"source":{"$ref":"#/components/schemas/SourceBlock","description":"Dataset provenance block (ingestion timestamp + source URL)."}},"type":"object","required":["clubs","total","source"],"title":"ClubListResponse"},"ClubSummary":{"properties":{"name":{"type":"string","title":"Name","description":"Club name as registered with the FFE."},"slug":{"type":"string","title":"Slug","description":"URL-safe slug derived from the club name. Use in ``GET /api/club/{slug}/players`` to fetch the club roster."},"player_count":{"type":"integer","minimum":0.0,"title":"Player Count","description":"Number of licensed players in the club for the current season."},"league":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"League","description":"Regional league code (e.g. IDF, BRE, NAQ)."},"federation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Federation","description":"Federation code (e.g. FRA)."}},"type":"object","required":["name","slug","player_count"],"title":"ClubSummary"},"ErrorResponse":{"properties":{"error":{"type":"string","title":"Error","description":"Short machine-readable error code."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Human-readable summary of the error."},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail","description":"Implementation-detail string. Only populated when ``ENVIRONMENT=development`` to avoid leaking internals."},"status":{"type":"integer","title":"Status","description":"HTTP status code (mirrors the response status)."},"retry_after_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Retry After Seconds","description":"Seconds the caller should wait before retrying (set on rate-limit responses)."},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Per-request correlation id. Pair with server logs when filing a support ticket."}},"type":"object","required":["error","status"],"title":"ErrorResponse","description":"Error envelope for non-2xx API responses.\n\nH5: every non-2xx body uses this shape regardless of which layer\nraised it (FFE* exceptions, ``HTTPException``,\n``RequestValidationError``, middleware short-circuits). The\n``request_id`` field mirrors the per-request id set by\n``RequestLoggingMiddleware`` so support requests can be cross-\nreferenced against server logs.","examples":[{"error":"missing_api_key","message":"An X-API-Key header is required for this endpoint.","request_id":"1a2b3c4d","status":403},{"error":"rate_limit_exceeded","message":"External callers are limited to 120 requests per minute.","request_id":"1a2b3c4d","retry_after_seconds":12,"status":429},{"detail":"[{'loc': ['query', 'last_name'], 'msg': 'string too short'}]","error":"validation_error","message":"Request parameters failed validation.","request_id":"1a2b3c4d","status":422}]},"FidePhotoBlock":{"properties":{"small":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Small","description":"URL of the small photo (Lichess CDN)."},"medium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Medium","description":"URL of the medium photo (Lichess CDN)."},"credit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credit","description":"Photo credit / attribution string."}},"type":"object","title":"FidePhotoBlock"},"FidePlayerBlock":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"FIDE title (e.g. \"GM\", \"IM\", \"FM\", \"CM\") or None if untitled."},"federation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Federation","description":"FIDE federation code (e.g. FRA, CHI, RUS)."},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year","description":"Birth year as recorded by FIDE."},"inactive":{"type":"boolean","title":"Inactive","description":"True when FIDE marks the player as inactive.","default":false},"standard":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Standard","description":"Current FIDE classical (standard) rating."},"rapid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rapid","description":"Current FIDE rapid rating."},"blitz":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blitz","description":"Current FIDE blitz rating."},"photo":{"anyOf":[{"$ref":"#/components/schemas/FidePhotoBlock"},{"type":"null"}],"description":"Photo URLs sourced from Lichess (may be None if not available)."},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex","description":"Sex code from the FIDE XML export: \"M\" (male) or \"F\" (female)."},"w_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"W Title","description":"Women's FIDE title (e.g. \"WGM\", \"WIM\", \"WFM\", \"WCM\") or None."},"k_factor":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K Factor","description":"FIDE K-factor (10, 20, or 40) as published in the monthly XML."}},"type":"object","title":"FidePlayerBlock"},"FidePlayerPublic":{"properties":{"fide_id":{"type":"integer","title":"Fide Id","description":"FIDE rating ID."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Full name as in FIDE list."},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"federation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Federation","description":"3-letter FIDE federation code (e.g. 'FRA')."},"standard":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Standard","description":"FIDE standard rating."},"rapid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rapid","description":"FIDE rapid rating."},"blitz":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blitz","description":"FIDE blitz rating."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"FIDE title (e.g. 'GM')."},"inactive":{"type":"boolean","title":"Inactive","description":"True when the player's most recent list entry is marked inactive.","default":false},"source":{"type":"string","const":"fide","title":"Source","description":"Provenance tag — always 'fide' for this endpoint.","default":"fide"}},"type":"object","required":["fide_id"],"title":"FidePlayerPublic","description":"Minimal FIDE player representation for cross-base search results."},"FideRatingPoint":{"properties":{"year":{"type":"integer","title":"Year","description":"Year of the published FIDE rating list."},"month":{"type":"integer","maximum":12.0,"minimum":1.0,"title":"Month","description":"Month of the published rating list (1–12)."},"rating":{"type":"integer","title":"Rating","description":"Published FIDE rating for that month."},"games":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Games","description":"Number of rated games played in that period (from FIDE XML where available)."}},"type":"object","required":["year","month","rating"],"title":"FideRatingPoint"},"FideRatingsBlock":{"properties":{"standard":{"items":{"$ref":"#/components/schemas/FideRatingPoint"},"type":"array","title":"Standard","description":"Monthly standard (classical) rating history, oldest first."},"rapid":{"items":{"$ref":"#/components/schemas/FideRatingPoint"},"type":"array","title":"Rapid","description":"Monthly rapid rating history, oldest first."},"blitz":{"items":{"$ref":"#/components/schemas/FideRatingPoint"},"type":"array","title":"Blitz","description":"Monthly blitz rating history, oldest first."}},"type":"object","title":"FideRatingsBlock"},"FideStatsBlock":{"properties":{"season_start":{"type":"string","title":"Season Start","description":"ISO date of the current FIDE season start (e.g. 2025-09-01). FIDE seasons run September–August."},"standard":{"$ref":"#/components/schemas/FideStatsDiscipline","description":"Season-to-date statistics for classical (standard) chess."},"rapid":{"$ref":"#/components/schemas/FideStatsDiscipline","description":"Season-to-date statistics for rapid chess."},"blitz":{"$ref":"#/components/schemas/FideStatsDiscipline","description":"Season-to-date statistics for blitz chess."}},"type":"object","required":["season_start","standard","rapid","blitz"],"title":"FideStatsBlock"},"FideStatsDiscipline":{"properties":{"peak":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Peak","description":"All-time peak FIDE rating for this discipline."},"ytd_delta":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ytd Delta","description":"Rating change from season start to the latest published list."},"last_published_delta":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Published Delta","description":"Rating change in the most recent published monthly list."},"games_ytd":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Games Ytd","description":"Number of rated games played since the current season start."},"first_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"First Year","description":"First year this player was rated in this discipline."}},"type":"object","title":"FideStatsDiscipline"},"LeagueListResponse":{"properties":{"leagues":{"items":{"$ref":"#/components/schemas/LeagueSummary"},"type":"array","title":"Leagues"},"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Number of league rows returned."}},"additionalProperties":false,"type":"object","required":["total"],"title":"LeagueListResponse","description":"Envelope for ``GET /api/league/list``."},"LeagueSummary":{"properties":{"code":{"type":"string","title":"Code","description":"League token as stored on player rows (filter key)."},"name":{"type":"string","title":"Name","description":"Display name (same as code when no separate label)."},"player_count":{"type":"integer","minimum":0.0,"title":"Player Count","description":"Players in this league."},"club_count":{"type":"integer","minimum":0.0,"title":"Club Count","description":"Distinct non-empty clubs in this league."}},"additionalProperties":false,"type":"object","required":["code","name","player_count","club_count"],"title":"LeagueSummary","description":"One regional league row."},"PlayerPublic":{"properties":{"id":{"type":"integer","title":"Id","description":"Internal database ID"},"ffe_id":{"type":"integer","title":"Ffe Id","description":"FFE player ID"},"last_name":{"type":"string","title":"Last Name","description":"Player's last name"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"Player's first name"},"gender":{"type":"integer","title":"Gender","description":"Gender code (0=unknown, 1=male, 2=female)"},"ffe_licence_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ffe Licence Number","description":"FFE licence number"},"ffe_licence":{"type":"integer","title":"Ffe Licence","description":"Papi FFE licence type code: 1=N (expired / non-renewed), 2=A (competition), 3=B (leisure). Served DB keeps A/B only after refresh."},"licence_type":{"anyOf":[{"type":"string","enum":["A","B"]},{"type":"null"}],"title":"Licence Type","description":"Derived from ``ffe_licence``: ``\"A\"`` when 2, ``\"B\"`` when 3, else null."},"federation":{"type":"string","title":"Federation","description":"Federation code (e.g., FRA)"},"league":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"League","description":"Regional league name"},"club":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Club","description":"Chess club name"},"fide_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fide Id","description":"FIDE player ID"},"fide_title":{"type":"integer","title":"Fide Title","description":"FIDE title code (0=none, 1=CM, 2=FM, 3=IM, 4=GM, etc.)"},"fide_title_label":{"type":"string","title":"Fide Title Label","description":"Mapped FIDE title (\"GM\", \"IM\", …) or \"\".","default":""},"birth_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Birth Year","description":"Year of birth derived from date_of_birth when present."},"category":{"type":"string","title":"Category","description":"FFE age category for the current season (e.g. SenM, Sen+F). Derived from completed age on **1 January** inside the Sep–Aug season (see FFE rules). Youth rows align with U8/U10/… bands; see README.","default":""},"is_active_this_season":{"type":"boolean","title":"Is Active This Season","description":"True when ``ffe_licence`` is active (2=A or 3=B).","default":false},"standard_rating":{"type":"integer","title":"Standard Rating","description":"Standard (classical) rating"},"rapid_rating":{"type":"integer","title":"Rapid Rating","description":"Rapid rating"},"blitz_rating":{"type":"integer","title":"Blitz Rating","description":"Blitz rating"},"standard_rating_type":{"type":"integer","title":"Standard Rating Type","description":"FFE/Papi standard rating type code (see ``*_rating_label``)."},"rapid_rating_type":{"type":"integer","title":"Rapid Rating Type","description":"FFE/Papi rapid rating type code (see ``*_rating_label``)."},"blitz_rating_type":{"type":"integer","title":"Blitz Rating Type","description":"FFE/Papi blitz rating type code (see ``*_rating_label``)."},"standard_rating_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Standard Rating Label","description":"Letter from ``*_rating_type``: 0/2→N, 1→E, 3→F."},"rapid_rating_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rapid Rating Label","description":"Letter from ``*_rating_type``: 0/2→N, 1→E, 3→F."},"blitz_rating_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blitz Rating Label","description":"Letter from ``*_rating_type``: 0/2→N, 1→E, 3→F."},"games_standard":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Games Standard","description":"Rated games (standard) when present in upstream DB."},"games_rapid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Games Rapid","description":"Rated games (rapid) when present in upstream DB."},"games_blitz":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Games Blitz","description":"Rated games (blitz) when present in upstream DB."},"fide":{"anyOf":[{"$ref":"#/components/schemas/FidePlayerBlock"},{"type":"null"}],"description":"Lichess FIDE snapshot (``?include=fide``)."},"fide_ratings":{"anyOf":[{"$ref":"#/components/schemas/FideRatingsBlock"},{"type":"null"}],"description":"Decoded rating history (``?include=fide_ratings``)."},"fide_stats":{"anyOf":[{"$ref":"#/components/schemas/FideStatsBlock"},{"type":"null"}],"description":"Persisted FIDE rollups (``?include=fide_stats``)."},"leaderboard_delta":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Leaderboard Delta","description":"Signed rating delta for ``GET /api/player/top`` responses only: either season-to-date gain or last FIDE publication step, matching the requested ``metric`` and ``discipline``."},"source":{"anyOf":[{"$ref":"#/components/schemas/SourceBlock"},{"type":"null"}],"description":"Per-response source block (always set on API responses)."}},"additionalProperties":true,"type":"object","required":["id","ffe_id","last_name","gender","ffe_licence","federation","fide_title","standard_rating","rapid_rating","blitz_rating","standard_rating_type","rapid_rating_type","blitz_rating_type"],"title":"PlayerPublic","description":"Default public payload (production default).\n\nM11: this schema is what every player route documents in OpenAPI so\nintegrators see the actual trimmed shape rather than the union with\nsensitive fields.\n\nThe runtime ``EXPOSE_SENSITIVE_FIELDS=true`` toggle is admin-only;\nwhen enabled, callers see the additional fields documented on\n``PlayerInternal``. Production deployments leave this off so only\n``PlayerPublic`` fields are returned.","examples":[{"birth_year":1990,"blitz_rating":1700,"blitz_rating_label":"E","blitz_rating_type":1,"category":"SenM","club":"Club d'Échecs de Paris","federation":"FRA","ffe_id":12345,"ffe_licence":2,"ffe_licence_number":"X00000","fide_id":123456,"fide_title":0,"fide_title_label":"","first_name":"John","gender":1,"id":1,"is_active_this_season":true,"last_name":"Doe","league":"Île-de-France","licence_type":"A","rapid_rating":1750,"rapid_rating_label":"E","rapid_rating_type":1,"source":{"ingested_at":"2026-05-10T22:00:00+00:00","url":"https://www.example.org/FicheJoueur.aspx?Ref=X00000"},"standard_rating":1800,"standard_rating_label":"E","standard_rating_type":1}]},"SourceBlock":{"properties":{"url":{"type":"string","title":"Url","description":"Deep link to the upstream player fiche. The provider URL is documented in LICENCE.md, not here.","examples":["https://upstream-source/players/X00000"]},"ingested_at":{"type":"string","title":"Ingested At","description":"ISO-8601 UTC ingestion timestamp.","examples":["2026-05-10T22:00:00+00:00"]}},"type":"object","required":["url","ingested_at"],"title":"SourceBlock","description":"Per-response provenance block.\n\n`provider` and `upstream_dataset` are intentionally absent — both\nlive in `LICENCE.md` only. The `url` deep-links to the FFE player\nfiche so integrators can route users to the canonical source."}},"securitySchemes":{"ChessXPApiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}}},"tags":[{"name":"players","description":"Player lookup by licence, internal ID, or FIDE ID, plus accent-insensitive name / club search."},{"name":"clubs","description":"FFE club directory and roster listings by stable URL slug."},{"name":"health","description":"Liveness/readiness probe with row count, last refresh timestamp, and staleness."},{"name":"root","description":"Service identity and links."}]}