Skip to main content
GET
/
api
/
tv
/
feed
Stream TV Channel Feed
curl --request GET \
  --url https://api.example.com/api/tv/feed
{
  "t": "<string>",
  "d": {
    "id": "<string>",
    "orientation": "<string>",
    "players": [
      {}
    ],
    "ply": 123,
    "uci": "<string>",
    "san": "<string>",
    "fen": "<string>",
    "clock": {},
    "winner": "<string>",
    "status": "<string>"
  }
}

Overview

Stream the events of the current TV game for a specific channel. This endpoint uses Server-Sent Events (SSE) to push updates in real-time as moves are played.

Endpoints

Stream Default Channel (Best)

GET https://lichess.org/api/tv/feed
Streams the “Best” channel (top-rated games).

Stream Specific Channel

GET https://lichess.org/api/tv/:channel/feed
Streams a specific TV channel.

Authentication

No authentication required. These are public endpoints.

Parameters

channel
string
The TV channel to stream. If omitted, defaults to “best”.Available channels:
  • best - Top Rated (2150+ standard games)
  • bullet - Bullet games
  • blitz - Blitz games
  • rapid - Rapid games
  • classical - Classical games
  • chess960 - Chess960 variant
  • kingOfTheHill - King of the Hill
  • threeCheck - Three-check
  • antichess - Antichess
  • atomic - Atomic
  • horde - Horde
  • racingKings - Racing Kings
  • crazyhouse - Crazyhouse
  • ultraBullet - UltraBullet
  • bot - Bot games
  • computer - Computer games
bc
boolean
default:"false"
Enable backwards compatibility mode. When true, uses Server-Sent Events format. When false (default), uses newline-delimited JSON (NDJSON).

Response Format

NDJSON Format (Default)

By default, the stream returns newline-delimited JSON. Each line is a complete JSON object representing a game event.

SSE Format

When bc=true, the stream uses Server-Sent Events format with text/event-stream content type.

Example Requests

cURL - Default Channel
curl https://lichess.org/api/tv/feed
cURL - Specific Channel
curl https://lichess.org/api/tv/blitz/feed
cURL - SSE Format
curl https://lichess.org/api/tv/feed?bc=true
JavaScript - NDJSON
const response = await fetch('https://lichess.org/api/tv/bullet/feed');
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  const lines = decoder.decode(value).split('\n');
  for (const line of lines) {
    if (line.trim()) {
      const event = JSON.parse(line);
      console.log('TV Update:', event);
    }
  }
}
JavaScript - EventSource (SSE)
const eventSource = new EventSource('https://lichess.org/api/tv/feed?bc=true');

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('TV Update:', data);
};

eventSource.onerror = (error) => {
  console.error('Connection error:', error);
  eventSource.close();
};
Python
import requests
import json

url = 'https://lichess.org/api/tv/blitz/feed'

with requests.get(url, stream=True) as response:
    for line in response.iter_lines():
        if line:
            event = json.loads(line)
            print('TV Update:', event)

Event Types

The stream emits various game-related events: When a new game becomes featured on the channel:
{
  "t": "featured",
  "d": {
    "id": "q7ZvsdUF",
    "orientation": "white",
    "players": [
      {
        "color": "white",
        "user": {
          "name": "Magnus",
          "id": "magnus",
          "title": "GM"
        },
        "rating": 2847
      },
      {
        "color": "black",
        "user": {
          "name": "Hikaru",
          "id": "hikaru",
          "title": "GM"
        },
        "rating": 2832
      }
    ]
  }
}

Move Event

When a move is played:
{
  "t": "ply",
  "d": {
    "ply": 23,
    "uci": "e2e4",
    "san": "e4",
    "fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
    "clock": {
      "white": 600,
      "black": 590
    }
  }
}

Game End Event

When the featured game ends:
{
  "t": "end",
  "d": {
    "winner": "white",
    "status": "mate"
  }
}

Response Fields

t
string
Event type: featured, ply, end, etc.
d
object
Event data - structure varies by event type

Connection Management

  • The stream is kept alive with periodic keep-alive messages
  • When a featured game ends, a new game from the channel will automatically be featured
  • Connections should be resilient to temporary disconnections and reconnect automatically
  • Games are refreshed based on activity - inactive games are replaced

Rate Limiting

  • Persistent connections are allowed but limited per IP address
  • Maximum of 4 concurrent streams per IP for standard users
  • Do not poll this endpoint - it’s designed to be streamed continuously

Channel Selection Criteria

Each channel selects games based on specific criteria:
  • Rating filters: Most channels require minimum ratings (e.g., Best: 2150+, Blitz: 2000+)
  • Time controls: Speed-based channels filter by game time control
  • Variants: Variant channels show only that specific chess variant
  • Recency: Games must have recent moves (freshness varies by channel)
  • Bot exclusion: Most channels exclude bot games (except the Bot channel)

Notes

  • The stream automatically switches to new games when the current one ends or becomes stale
  • Not all channels have active games at all times
  • The default channel (“best”) typically has the most consistent availability
  • Use NDJSON format (default) for simpler parsing in modern applications

Source Code Reference

  • Controller: app/controllers/Tv.scala:92-108
  • Routes: conf/routes:43-44

Build docs developers (and LLMs) love