# ChessXP FFE Player API

OpenAPI **3.1.0** — markdown mirror for agents.

**ChessXP FFE Player API** — read-only access to the French chess
player roster.

### What you get

For each player, the public payload exposes (default, post-trim):
``id``, ``ffe_id``, ``last_name``, ``first_name``, ``gender``,
``ffe_licence_number``, ``ffe_licence`` (Papi type: 1=N expired, 2=A, 3=B),
``licence_type`` (``"A"`` / ``"B"`` / null), ``federation``,
``league``, ``club``, ``fide_id``, ``fide_title``, ``birth_year``,
``category``, ``is_active_this_season``, the three rating buckets
(``standard_rating``, ``rapid_rating``, ``blitz_rating``) and their
``*_type`` / ``*_label`` flags, ``games_*`` (when available), and a
``source`` block with the upstream deep link plus the ingestion
timestamp.

``date_of_birth`` and ``city`` are excluded in the default deployment
to minimise personal data. Dataset provenance, provider name, and
upstream dataset URL are documented once in ``LICENCE.md`` (supplied
with your API access materials) — they are not repeated in OpenAPI or
per-response metadata.

### Optional FIDE blocks (``?include=``)

Append ``?include=<tokens>`` (comma-separated) to any single-player
or batch / search endpoint to attach additional data sourced from FIDE
monthly rating lists:

| Token | What you get |
|---|---|
| ``fide`` | FIDE profile — title, federation, current ratings, photo, sex, women's title, K-factor |
| ``fide_ratings`` | Full monthly rating history (standard / rapid / blitz) with game counts |
| ``fide_stats`` | Season-to-date stats — peak, YTD delta, last-published delta, games YTD, first year |

> **Note:** FIDE blocks require the player to have a ``fide_id`` and a
> cached FIDE entry (populated by the weekly refresh pipeline).
> Players without a FIDE ID or with no cache entry silently omit the
> requested blocks rather than returning an error.

### Authentication

Every protected endpoint expects an ``X-API-Key`` header. Click
**Authorize** in the top-right and paste your key. Need one? Reach
out via the lead form on
[api-ffe.chessxp.com](https://api-ffe.chessxp.com).

### Rate limits

External keys: **120 requests / minute** (sliding window). When you
hit the limit you receive a ``429`` with a ``Retry-After`` header.
ChessXP internal keys bypass this limit.

### Refresh cadence

The dataset refreshes weekly (Monday 03:00 Europe/Paris). The public
``/health`` probe publishes ``row_count``, ``last_refresh_utc``, and
``stale_days_since_refresh`` so you can verify currency without an API
key. ChessXP operators also use an authenticated verbose health route
that is intentionally omitted from this schema.

### Quick start

> L5: the licence ``X00000`` below is a **synthetic placeholder** —
> replace it with a real FFE licence number when calling the API.

```bash
curl -fsS \
  -H "X-API-Key: <your-key>" \
  https://ffe.chessxp.com/api/player/X00000
```

```javascript
const r = await fetch(
  "https://ffe.chessxp.com/api/player/X00000",
  { headers: { "X-API-Key": process.env.FFE_KEY } },
);
const player = await r.json();
```

```python
import requests
r = requests.get(
    "https://ffe.chessxp.com/api/player/X00000",
    headers={"X-API-Key": API_KEY},
    timeout=10,
)
r.raise_for_status()
print(r.json())
```

## `GET /`

Service identity

## `GET /api/club/list`

List FFE clubs

**Parameters**
- `league` (query) — any
- `federation` (query) — any
- `limit` (query) — integer
- `offset` (query) — integer

## `GET /api/club/{slug}/players`

Players in a club (by slug)

**Parameters**
- `slug` (path*) — string
- `include` (query) — any

## `GET /api/fide/search-by-name`

Search FIDE players by name (cross-base)

**Parameters**
- `last_name` (query*) — string
- `first_name` (query) — any
- `federation` (query) — any
- `limit` (query) — integer

## `GET /api/league/list`

List FFE regional leagues

## `GET /api/player/batch`

Batch lookup by FFE licence numbers

**Parameters**
- `licences` (query*) — string
- `include` (query) — any

## `GET /api/player/fide/{fide_id}`

Get player by FIDE ID

**Parameters**
- `fide_id` (path*) — integer
- `include` (query) — any

## `GET /api/player/id/{player_id}`

Get player by internal ID

**Parameters**
- `player_id` (path*) — integer
- `include` (query) — any

## `GET /api/player/search`

Search players by name (legacy)

**Parameters**
- `name` (query*) — string
- `limit` (query) — integer
- `league` (query) — any
- `federation` (query) — any
- `min_rating` (query) — any
- `max_rating` (query) — any
- `category` (query) — any
- `include` (query) — any

## `GET /api/player/search-by-club`

Search players by club name

**Parameters**
- `club` (query*) — string
- `limit` (query) — integer
- `league` (query) — any
- `federation` (query) — any
- `min_rating` (query) — any
- `max_rating` (query) — any
- `category` (query) — any
- `match` (query) — any
- `include_inactive` (query) — boolean
- `include_unrated` (query) — boolean
- `fide_limit` (query) — integer
- `include` (query) — any

## `GET /api/player/search-by-name`

Search players by last + first name

**Parameters**
- `last_name` (query*) — string
- `first_name` (query) — any
- `limit` (query) — integer
- `league` (query) — any
- `federation` (query) — any
- `min_rating` (query) — any
- `max_rating` (query) — any
- `category` (query) — any
- `match` (query) — any
- `include_inactive` (query) — boolean
- `include_unrated` (query) — boolean
- `fide_limit` (query) — integer
- `cross` (query) — boolean
- `include` (query) — any

## `GET /api/player/{licence_number}`

Get player by FFE licence number

**Parameters**
- `licence_number` (path*) — string
- `include` (query) — any

## `GET /api/players/top`

Top players by FIDE rating delta

**Parameters**
- `metric` (query*) — string
- `discipline` (query) — string
- `league` (query) — any
- `federation` (query) — any
- `club_slug` (query) — any
- `category` (query) — any
- `limit` (query) — integer
- `include` (query) — any

## `GET /health`

Liveness / readiness probe

---

Machine-readable source: `/openapi.json`.