F1 RELAY Live F1 flag and session state over one WebSocket
github.com/joppedc/f1-relay
Connecting
IDLE Stale Override · test
Last frame
Connect
GET /state — one-shot poll, same JSON body
GET /llms.txt — this protocol, machine-readable
Live frame updates in place
waiting…

What this is

F1 Relay connects to Formula 1's live timing feed and re-broadcasts the parts that describe what is happening on track — flags, safety car, session phase — over one WebSocket. The relay absorbs upstream protocol changes so clients do not have to.

This document is the complete client contract. It is served as HTML at / and as Markdown at /llms.txt. Paths below are relative to the origin serving it.

Connect

Open a WebSocket to /ws. Use wss:// if this document was served over HTTPS, ws:// otherwise.

const ws = new WebSocket('wss://relay.example/ws');
ws.onmessage = (e) => {
  const s = JSON.parse(e.data);
  console.log(s.display, s.session, s.name);
};

You receive the full current state immediately on connect, and a new message every time it changes. No subscription handshake, no client-to-server messages — the relay ignores anything you send, and closes the connection if a single frame you send exceeds 1 KB.

If a WebSocket is impractical, poll GET /state instead. It returns the exact same object.

curl https://relay.example/state

Message shape

Every message is the complete state. There are no diffs and no partial updates, so applying one is a straight overwrite with nothing to reconcile.

{"v":1,"display":"SC","track":"SC","session":"live","type":"Race",
 "name":"Hungarian Grand Prix","stale":false,"override":false,
 "ts":"2026-07-26T13:40:00Z"}
FieldTypeMeaning
vnumberProtocol version. Increments only on breaking changes.
displaystringTrack status and session phase resolved into one actionable value. If your client shows a single state, use this field alone.
trackstring or nullRaw track status, unresolved.
sessionstring or nullSession phase.
typestring or nullSession label, e.g. Race, Qualifying, Practice 2.
namestring or nullMeeting name, e.g. Hungarian Grand Prix.
stalebooleantrue while the relay's link to F1 is down. The values shown are last-known, not current.
overridebooleantrue while an operator test broadcast is active. Not a real session event.
tsstringMessage timestamp, ISO 8601.

Clients must ignore unknown fields. New fields can be added within v: 1.

display

The resolved value, and the only field most clients need. Exactly one of:

ValueMeaning
REDRed flag. Session stopped.
SCSafety car deployed.
VSCVirtual safety car.
YELLOWYellow flag.
CLEARGreen. Session running, track clear.
CHEQChequered flag. Race or sprint finished.
IDLENothing to show. No session running, or a session type with no track state worth displaying.

IDLE is the normal resting value — F1 runs a few hours a fortnight, so a client will sit in IDLE the overwhelming majority of the time. Design for it as the default, not as an error.

Resolution happens in priority order: a red flag during a live or suspended session wins over everything; then during a live session the track status maps straight through; then a finished or finalised race or sprint gives CHEQ; otherwise IDLE. Practice and qualifying never produce CHEQ.

track

The raw track status, before resolution. Useful if you want to distinguish conditions that display collapses. null until the relay has seen one.

ValueMeaning
CLEARTrack clear.
YELLOWYellow or double yellow.
SCSafety car.
VSCVirtual safety car, including the "ending" phase.
REDRed flag.

session

The session phase. null until the relay has seen one.

ValueMeaning
preBefore the session starts.
liveSession running.
suspendedStopped under a red flag, expected to resume.
finishedChequered flag shown.
finalisedResults confirmed.
endedBroadcast wrapped up.
breakBetween sessions of the same meeting.

Staleness

When the relay loses its upstream link, it keeps serving the last-known state with stale: true rather than disconnecting you or sending nulls. Treat a stale message as "this was true recently" — how long you keep trusting it is your call. ts tells you when the message was sent, not when the state last genuinely changed.

The relay sends a message when stale flips, so you will be told promptly in both directions.

Reconnecting

Reconnect with backoff on close. Because every message is full state, there is nothing to resync — connect, take the first message, carry on.

The relay pings every 30 seconds and drops clients that miss two consecutive pongs. Browser WebSocket clients answer automatically. If you are writing a native or embedded client, make sure your library replies to pings.

A close with code 1013 means the relay is at its subscriber limit. Back off further and retry.

Other endpoints

EndpointPurpose
GET /wsThe subscriber WebSocket.
GET /stateCurrent state as JSON. Same object as a WebSocket message.
GET /healthzLiveness: {ok, f1, uptimeS}. 200 while the process is up, regardless of upstream.
GET /llms.txtThis document, as Markdown.

/state and /healthz send Access-Control-Allow-Origin: *, so browser apps can call them directly.