openapi: 3.1.0 info: version: 2.0.144 title: Lichess.org API reference Account Bulk pairings API contact: name: Lichess.org API url: https://lichess.org/api email: contact@lichess.org x-logo: url: https://lichess1.org/assets/logo/lichess-pad12.svg license: name: AGPL-3.0-or-later url: https://www.gnu.org/licenses/agpl-3.0.txt description: '# Introduction Welcome to the reference for the Lichess API! Lichess is free/libre, open-source chess server powered by volunteers and donations. - Get help in the [Lichess Discord channel](https://discord.gg/lichess) - API demo app with OAuth2 login and gameplay: [source](https://github.com/lichess-org/api-demo) / [demo](https://lichess-org.github.io/api-demo/) - API UI app with OAuth2 login and endpoint forms: [source](https://github.com/lichess-org/api-ui) / [website](https://lichess.org/api/ui) - [Contribute to this documentation on Github](https://github.com/lichess-org/api) - Check out [Lichess widgets to embed in your website](https://lichess.org/developers) - [Download all Lichess rated games](https://database.lichess.org/) - [Download all Lichess puzzles with themes, ratings and votes](https://database.lichess.org/#puzzles) - [Download all evaluated positions](https://database.lichess.org/#evals) ## Endpoint All requests go to `https://lichess.org` (unless otherwise specified). ## Clients - [Python general API](https://github.com/lichess-org/berserk) - [MicroPython general API](https://github.com/mkomon/uberserk) - [Python general API - async](https://pypi.org/project/async-lichess-sdk) - [Python Lichess Bot](https://github.com/lichess-bot-devs/lichess-bot) - [Python Board API for Certabo](https://github.com/haklein/certabo-lichess) - [Java general API](https://github.com/tors42/chariot) - [JavaScript & TypeScript general API](https://github.com/devjiwonchoi/equine) - [LichessNET - C# API Wrapper](https://github.com/Rabergsel/LichessNET) - [.NET general API](https://github.com/Dblike/LichessSharp) ## Rate limiting All requests are rate limited using various strategies, to ensure the API remains responsive for everyone. Only make one request at a time. If you receive an HTTP response with a [429 status](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429), you have exceded one of the rate limits. In most cases, waiting one minute before retrying will be sufficient, but some limits may require longer. Reduce your request frequency before retrying. ## Streaming with ND-JSON Some API endpoints stream their responses as [Newline Delimited JSON a.k.a. **nd-json**](https://github.com/ndjson/ndjson-spec), with one JSON object per line. Here''s a [JavaScript utility function](https://gist.github.com/ornicar/a097406810939cf7be1df8ea30e94f3e) to help reading NDJSON streamed responses. ## Authentication ### Which authentication method is right for me? [Read about the Lichess API authentication methods and code examples](https://github.com/lichess-org/api/blob/master/example/README.md) ### Personal Access Token Personal API access tokens allow you to quickly interact with Lichess API without going through an OAuth flow. - [Generate a personal access token](https://lichess.org/account/oauth/token) - `curl https://lichess.org/api/account -H "Authorization: Bearer {token}"` - [NodeJS example](https://github.com/lichess-org/api/tree/master/example/oauth-personal-token) ### Token Security - Keep your tokens secret. Do not share them in public repositories or public forums. - Your tokens can be used to make your account perform arbitrary actions (within the limits of the tokens'' scope). You remain responsible for all activities on your account. - Do not hardcode tokens in your application''s code. Use environment variables or a secure storage and ensure they are not shipped/exposed to users. Be especially careful that they are not included in frontend bundles or apps that are shipped to users. - If you suspect a token has been compromised, revoke it immediately. To see your active tokens or revoke them, see [your Personal API access tokens](https://lichess.org/account/oauth/token). ### Authorization Code Flow with PKCE The authorization code flow with PKCE allows your users to **login with Lichess**. Lichess supports unregistered and public clients (no client authentication, choose any unique client id). The only accepted code challenge method is `S256`. Access tokens are long-lived (expect one year), unless they are revoked. Refresh tokens are not supported. See the [documentation for the OAuth endpoints](#tag/OAuth) or the [PKCE RFC](https://datatracker.ietf.org/doc/html/rfc7636#section-4) for a precise protocol description. - [Demo app](https://lichess-org.github.io/api-demo/) - [Minimal client-side example](https://github.com/lichess-org/api/tree/master/example/oauth-app) - [Flask/Python example](https://github.com/lakinwecker/lichess-oauth-flask) - [Java example](https://github.com/tors42/lichess-oauth-pkce-app) - [NodeJS Passport strategy to login with Lichess OAuth2](https://www.npmjs.com/package/passport-lichess) #### Real life examples - [PyChess](https://github.com/gbtami/pychess-variants) ([source code](https://github.com/gbtami/pychess-variants)) - [Lichess4545](https://www.lichess4545.com/) ([source code](https://github.com/cyanfish/heltour)) - [English Chess Federation](https://ecf.octoknight.com/) - [Rotherham Online Chess](https://rotherhamonlinechess.azurewebsites.net/tournaments) ### Token format Access tokens and authorization codes match `^[A-Za-z0-9_]+$`. The length of tokens can be increased without notice. Make sure your application can handle at least 512 characters. By convention tokens have a recognizable prefix, but do not rely on this. ' servers: - url: https://lichess.org - url: https://lichess.dev - url: http://localhost:{port} variables: port: default: '8080' - url: http://l.org tags: - name: Bulk pairings description: 'Create many games for other players. These endpoints are intended for tournament organisers. ' paths: /api/bulk-pairing: get: operationId: bulkPairingList summary: View your bulk pairings description: 'Get a list of bulk pairings you created. ' tags: - Bulk pairings security: - OAuth2: - challenge:bulk responses: '200': description: The list of bulk pairing the logged in user created. headers: Access-Control-Allow-Origin: schema: type: string default: '''*''' content: application/json: schema: type: array items: $ref: '#/components/schemas/BulkPairing' post: operationId: bulkPairingCreate summary: Create a bulk pairing description: "Schedule many games at once, up to 24h in advance.\nOAuth tokens are required for all paired players, with the `challenge:write` scope.\nYou can schedule up to 500 games every 10 minutes. [Contact us](mailto:contact@lichess.org) if you need higher limits.\nIf games have a real-time clock, each player must have only one pairing.\nFor correspondence games, players can have multiple pairings within the same bulk.\n\n**The entire bulk is rejected if:**\n - a token is missing\n - a token is present more than once (except in correspondence)\n - a token lacks the `challenge:write` scope\n - a player account is closed\n - a player is paired more than once (except in correspondence)\n - a bulk is already scheduled to start at the same time with the same player\n - you have 20 scheduled bulks\n - you have 1000 scheduled games\n\nPartial bulks are never created. Either it all fails, or it all succeeds.\nWhen it fails, it does so with an error message explaining the issue.\nFailed bulks are not counted in the rate limiting, they are free.\nFix the issues, manually or programmatically, then retry to schedule the bulk.\nA successful bulk creation returns a JSON bulk document. Its ID can be used for further operations.\n" tags: - Bulk pairings security: - OAuth2: - challenge:bulk requestBody: description: Parameters of the pairings required: true content: application/x-www-form-urlencoded: schema: type: object properties: players: type: string description: 'OAuth tokens of all the players to pair, with the syntax `tokenOfWhitePlayerInGame1:tokenOfBlackPlayerInGame1,tokenOfWhitePlayerInGame2:tokenOfBlackPlayerInGame2,...`. The 2 tokens of the players of a game are separated with `:`. The first token gets the white pieces. Games are separated with `,`. Up to 1000 tokens can be sent, for a max of 500 games. Each token must be included at most once. Example: `token1:token2,token3:token4,token5:token6` ' clock.limit: type: integer description: 'Clock initial time in seconds. Example: `600` ' minimum: 0 maximum: 10800 clock.increment: type: integer description: 'Clock increment in seconds. Example: `2` ' minimum: 0 maximum: 60 days: type: integer description: Days per turn. For correspondence games only. enum: - 1 - 2 - 3 - 5 - 7 - 10 - 14 pairAt: type: integer format: int64 description: 'Date at which the games will be created as a Unix timestamp in milliseconds. Up to 7 days in the future. Omit, or set to current date and time, to start the games immediately. Example: `1612289869919` ' startClocksAt: type: integer format: int64 description: 'Date at which the clocks will be automatically started as a Unix timestamp in milliseconds. Up to 7 days in the future. Note that the clocks can start earlier than specified, if players start making moves in the game. If omitted, the clocks will not start automatically. Example: `1612289869919` ' rated: type: boolean description: Game is rated and impacts players ratings default: false variant: $ref: '#/components/schemas/VariantKey' fen: $ref: '#/components/schemas/FromPositionFEN' message: type: string description: 'Message that will be sent to each player, when the game is created. It is sent from your user account. `{opponent}` and `{game}` are placeholders that will be replaced with the opponent and the game URLs. You can omit this field to send the default message, but if you set your own message, it must at least contain the `{game}` placeholder. ' default: 'Your game with {opponent} is ready: {game}.' rules: type: string enum: - noAbort - noRematch - noGiveTime - noClaimWin - noEarlyDraw description: 'Extra game rules separated by commas. Example: `noAbort,noRematch` ' responses: '200': description: The bulk pairing has been successfully created. headers: Access-Control-Allow-Origin: schema: type: string default: '''*''' content: application/json: schema: $ref: '#/components/schemas/BulkPairing' '400': description: The creation of the bulk pairings failed. content: application/json: schema: $ref: '#/components/schemas/Error' /api/bulk-pairing/{id}/start-clocks: post: operationId: bulkPairingStartClocks summary: Manually start clocks description: 'Immediately start all clocks of the games of a bulk pairing. This overrides the `startClocksAt` value of an existing bulk pairing. If the games have not yet been created (`bulk.pairAt` is in the future), then this does nothing. If the clocks have already started (`bulk.startClocksAt` is in the past), then this does nothing. ' tags: - Bulk pairings security: - OAuth2: - challenge:bulk parameters: - in: path name: id schema: type: string description: The ID of the bulk pairing example: 5IrD6Gzz required: true responses: '200': description: The clocks of the games of a bulk pairing were successfully started. headers: Access-Control-Allow-Origin: schema: type: string default: '''*''' content: application/json: schema: $ref: '#/components/schemas/Ok' '404': description: The bulk pairing was not found. content: application/json: schema: $ref: '#/components/schemas/NotFound' /api/bulk-pairing/{id}: get: operationId: bulkPairingGet summary: Show a bulk pairing description: 'Get a single bulk pairing by its ID. ' tags: - Bulk pairings security: - OAuth2: - challenge:bulk parameters: - in: path name: id schema: type: string description: The ID of the bulk pairing example: 5IrD6Gzz required: true responses: '200': description: The bulk pairing. headers: Access-Control-Allow-Origin: schema: type: string default: '''*''' content: application/json: schema: $ref: '#/components/schemas/BulkPairing' '404': description: The bulk pairing was not found. content: application/json: schema: $ref: '#/components/schemas/NotFound' delete: operationId: bulkPairingDelete summary: Cancel a bulk pairing description: 'Cancel and delete a bulk pairing that is scheduled in the future. If the games have already been created, then this does nothing. Canceling a bulk pairing does not refund the rate limit cost of that bulk pairing. ' tags: - Bulk pairings security: - OAuth2: - challenge:bulk parameters: - in: path name: id schema: type: string description: The ID of the bulk pairing example: 5IrD6Gzz required: true responses: '200': description: The bulk pairing was successfully deleted. headers: Access-Control-Allow-Origin: schema: type: string default: '''*''' content: application/json: schema: $ref: '#/components/schemas/Ok' '404': description: The bulk pairing to delete was not found. content: application/json: schema: $ref: '#/components/schemas/NotFound' /api/bulk-pairing/{id}/games: get: operationId: bulkPairingIdGamesGet summary: Export games of a bulk pairing description: 'Download games of a bulk in PGN or [ndjson](#description/streaming-with-nd-json) format, depending on the request `Accept` header. ' tags: - Bulk pairings security: - OAuth2: - challenge:bulk parameters: - $ref: '#/components/parameters/AcceptPgnOrNdjson' - in: path name: id schema: type: string description: The ID of the bulk pairing example: 5IrD6Gzz required: true - in: query name: moves description: Include the PGN moves. schema: type: boolean default: true - in: query name: pgnInJson description: Include the full PGN within the JSON response, in a `pgn` field. schema: type: boolean default: false - in: query name: tags description: Include the PGN tags. schema: type: boolean default: true - in: query name: clocks description: 'Include clock status when available. Either as PGN comments: `2. exd5 { [%clk 1:01:27] } e5 { [%clk 1:01:28] }` Or in a `clocks` JSON field, as centisecond integers, depending on the response type. ' schema: type: boolean default: false - in: query name: evals description: 'Include analysis evaluations and comments, when available. Either as PGN comments: `12. Bxf6 { [%eval 0.23] } a3 { [%eval -1.09] }` Or in an `analysis` JSON field, depending on the response type. ' schema: type: boolean default: false - in: query name: accuracy description: 'Include [accuracy percent](https://lichess.org/page/accuracy) of each player, when available. Only available in JSON. ' schema: type: boolean default: false - in: query name: opening description: 'Include the opening name. Example: `[Opening "King''s Gambit Accepted, King''s Knight Gambit"]` ' schema: type: boolean default: false - in: query name: division description: 'Plies which mark the beginning of the middlegame and endgame. Only available in JSON ' schema: type: boolean default: false - in: query name: literate description: 'Insert textual annotations in the PGN about the opening, analysis variations, mistakes, and game termination. Example: `5... g4? { (-0.98 → 0.60) Mistake. Best move was h6. } (5... h6 6. d4 Ne7 7. g3 d5 8. exd5 fxg3 9. hxg3 c6 10. dxc6)` ' schema: type: boolean default: false responses: '200': description: The representation of the games. headers: Access-Control-Allow-Origin: schema: type: string default: '''*''' content: application/x-chess-pgn: schema: $ref: '#/components/schemas/GamePgn' application/x-ndjson: schema: $ref: '#/components/schemas/GameJson' components: schemas: GameJson: type: object properties: id: type: string rated: type: boolean variant: $ref: '#/components/schemas/VariantKey' speed: $ref: '#/components/schemas/Speed' perf: type: string createdAt: type: integer format: int64 lastMoveAt: type: integer format: int64 status: $ref: '#/components/schemas/GameStatusName' source: type: string players: $ref: '#/components/schemas/GamePlayers' initialFen: type: string winner: $ref: '#/components/schemas/GameColor' opening: $ref: '#/components/schemas/GameOpening' moves: type: string pgn: type: string daysPerTurn: type: integer analysis: type: array items: $ref: '#/components/schemas/GameMoveAnalysis' tournament: type: string swiss: type: string clock: type: object properties: initial: type: integer increment: type: integer totalTime: type: integer required: - initial - increment - totalTime clocks: type: array items: type: integer division: type: object properties: middle: type: integer description: Ply at which the middlegame begins end: type: integer description: Ply at which the endgame begins required: [] required: - id - rated - variant - speed - perf - createdAt - lastMoveAt - status - players PatronColor: type: integer description: 'Players can choose a color for their Patron wings. See [here for the color mappings](https://github.com/lichess-org/lila/blob/master/ui/lib/css/abstract/_patron-colors.scss). The presence of this field indicates the player is an active Patron. ' minimum: 1 maximum: 10 FromPositionFEN: type: string description: Custom initial position (in X-FEN). Variant must be standard, fromPosition, or chess960 (if a valid 960 starting position), and the game cannot be rated. default: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 Patron: type: boolean deprecated: true description: 'Use patronColor value instead to determine if player is a patron. ' VariantKey: type: string enum: - standard - chess960 - crazyhouse - antichess - atomic - horde - kingOfTheHill - racingKings - threeCheck - fromPosition example: standard default: standard GameMoveAnalysis: type: object properties: eval: type: integer description: Evaluation in centipawns mate: type: integer description: Number of moves until forced mate best: type: string example: c2c3 description: Best move in UCI notation (only if played move was inaccurate) variation: type: string example: c3 Nc6 d4 Qb6 Be2 Nge7 Na3 cxd4 cxd4 Nf5 description: Best variation in SAN notation (only if played move was inaccurate) judgment: type: object description: Judgment annotation (only if played move was inaccurate) properties: name: type: string enum: - Inaccuracy - Mistake - Blunder comment: type: string example: Blunder. Nxg6 was best. GamePgn: type: string example: '[Event "Rated Blitz game"] [Site "https://lichess.org/fY44h4OY"] [Date "2018.03.29"] [Round "-"] [White "pveldman"] [Black "thibault"] [Result "1-0"] [UTCDate "2018.03.29"] [UTCTime "01:38:15"] [WhiteElo "1610"] [BlackElo "1601"] [WhiteRatingDiff "+10"] [BlackRatingDiff "-10"] [Variant "Standard"] [TimeControl "180+0"] [ECO "C62"] [Opening "Ruy Lopez: Steinitz Defense"] [Termination "Normal"] 1. e4 { [%clk 0:03:00] } e5 { [%clk 0:03:00] } 2. Nf3 { [%clk 0:02:59] } Nc6 { [%clk 0:02:58] } 3. Bb5 { [%clk 0:02:57] } d6 { [%clk 0:02:55] } 4. h3 { [%clk 0:02:54] } Nf6 { [%clk 0:02:52] } 5. Bxc6+ { [%clk 0:02:52] } bxc6 { [%clk 0:02:49] } 6. d3 { [%clk 0:02:51] } Be7 { [%clk 0:02:46] } 7. O-O { [%clk 0:02:47] } O-O { [%clk 0:02:45] } 8. b3 { [%clk 0:02:45] } d5 { [%clk 0:02:45] } 9. exd5 { [%clk 0:02:33] } cxd5 { [%clk 0:02:40] } 10. Nxe5 { [%clk 0:02:31] } Qd6 { [%clk 0:02:38] } 1-0 ' NotFound: properties: error: type: string required: - error example: error: Not found. GameColor: type: string enum: - white - black Ok: properties: ok: type: boolean required: - ok GameOpening: type: object properties: eco: type: string name: type: string ply: type: integer required: - eco - name - ply GamePlayers: type: object properties: white: $ref: '#/components/schemas/GamePlayerUser' black: $ref: '#/components/schemas/GamePlayerUser' required: - white - black Clock: type: object properties: limit: type: integer increment: type: integer required: - limit - increment Flair: type: string description: See [available flair list and images](https://github.com/lichess-org/lila/tree/master/public/flair) Error: type: object properties: error: type: string description: The cause of the error. required: - error example: error: This request is invalid because [...] GameStatusName: type: string enum: - created - started - aborted - mate - resign - stalemate - timeout - draw - outoftime - cheat - noStart - unknownFinish - insufficientMaterialClaim - variantEnd Speed: type: string enum: - ultraBullet - bullet - blitz - rapid - classical - correspondence BulkPairing: type: object properties: id: type: string games: type: array items: type: object properties: id: type: string black: type: string white: type: string variant: $ref: '#/components/schemas/VariantKey' clock: $ref: '#/components/schemas/Clock' pairAt: type: integer pairedAt: type: - integer - 'null' rated: type: boolean startClocksAt: type: integer scheduledAt: type: integer required: - id - games - variant - clock - pairAt - pairedAt - rated - startClocksAt - scheduledAt example: id: RVAcwgg7 games: - id: NKop9IyD black: lizen1 white: thibault - id: KT8374ut black: lizen3 white: lizen2 - id: wInQr8Sk black: lizen5 white: lizen4 variant: standard clock: increment: 0 limit: 300 pairAt: 1612289869919 pairedAt: null rated: false startClocksAt: 1612200422971 scheduledAt: 1612203514628 LightUser: type: object properties: id: type: string name: type: string flair: $ref: '#/components/schemas/Flair' title: $ref: '#/components/schemas/Title' patron: $ref: '#/components/schemas/Patron' patronColor: $ref: '#/components/schemas/PatronColor' required: - id - name GamePlayerUser: type: object properties: user: $ref: '#/components/schemas/LightUser' rating: type: integer ratingDiff: type: integer name: type: string provisional: type: boolean aiLevel: type: integer analysis: type: object properties: inaccuracy: type: integer mistake: type: integer blunder: type: integer acpl: type: integer accuracy: type: integer required: - inaccuracy - mistake - blunder - acpl team: type: string required: - user - rating Title: type: string enum: - GM - WGM - IM - WIM - FM - WFM - NM - CM - WCM - WNM - LM - BOT description: only appears if the user is a titled player or a bot user parameters: AcceptPgnOrNdjson: in: header name: Accept description: 'Specify the desired response format. Use `application/x-chess-pgn` to get the games in PGN format. Use `application/x-ndjson` to get the games in ndjson format. [Read about ndjson here](#description/streaming-with-nd-json) and how you can parse it in Javascript. ' schema: type: string enum: - application/x-chess-pgn - application/x-ndjson default: application/x-chess-pgn securitySchemes: OAuth2: type: oauth2 description: 'Read [the introduction for how to make authenticated requests](#description/authentication). ' flows: authorizationCode: authorizationUrl: https://lichess.org/oauth tokenUrl: https://lichess.org/api/token scopes: preference:read: Read your preferences preference:write: Write your preferences email:read: Read your email address engine:read: Read your external engines engine:write: Create, update, delete your external engines challenge:read: Read incoming challenges challenge:write: Create, accept, decline challenges challenge:bulk: Create, delete, query bulk pairings study:read: Read private studies and broadcasts study:write: Create, update, delete studies and broadcasts tournament:write: Create tournaments racer:write: Create and join puzzle races puzzle:read: Read puzzle activity puzzle:write: Write puzzle activity team:read: Read private team information team:write: Join, leave teams team:lead: Manage teams (kick members, send PMs) follow:read: Read followed players follow:write: Follow and unfollow other players msg:write: Send private messages to other players board:play: Play with the Board API bot:play: Play with the Bot API. Only for [Bot accounts](#tag/bot/POST/api/bot/account/upgrade) web:mod: Use moderator tools (within the bounds of your permissions)