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"}
| Field | Type | Meaning |
|---|---|---|
v | number | Protocol version. Increments only on breaking changes. |
display | string | Track status and session phase resolved into one actionable value. If your client shows a single state, use this field alone. |
track | string or null | Raw track status, unresolved. |
session | string or null | Session phase. |
type | string or null | Session label, e.g. Race, Qualifying, Practice 2. |
name | string or null | Meeting name, e.g. Hungarian Grand Prix. |
stale | boolean | true while the relay's link to F1 is down. The values shown are last-known, not current. |
override | boolean | true while an operator test broadcast is active. Not a real session event. |
ts | string | Message 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:
| Value | Meaning |
|---|---|
RED | Red flag. Session stopped. |
SC | Safety car deployed. |
VSC | Virtual safety car. |
YELLOW | Yellow flag. |
CLEAR | Green. Session running, track clear. |
CHEQ | Chequered flag. Race or sprint finished. |
IDLE | Nothing 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.
| Value | Meaning |
|---|---|
CLEAR | Track clear. |
YELLOW | Yellow or double yellow. |
SC | Safety car. |
VSC | Virtual safety car, including the "ending" phase. |
RED | Red flag. |
session
The session phase. null until the relay has seen one.
| Value | Meaning |
|---|---|
pre | Before the session starts. |
live | Session running. |
suspended | Stopped under a red flag, expected to resume. |
finished | Chequered flag shown. |
finalised | Results confirmed. |
ended | Broadcast wrapped up. |
break | Between 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
| Endpoint | Purpose |
|---|---|
GET /ws | The subscriber WebSocket. |
GET /state | Current state as JSON. Same object as a WebSocket message. |
GET /healthz | Liveness: {ok, f1, uptimeS}. 200 while the process is up, regardless of upstream. |
GET /llms.txt | This document, as Markdown. |
/state and /healthz send Access-Control-Allow-Origin: *, so browser apps can call them directly.