Kels Telemetry

Live data feed

75 channels straight off the car's CAN bus, decoded, scaled, and pushed to any client that connects. Ten updates a second, typically under 300 bytes each. No key, no rate limit, no SDK.

Endpoints

EndpointWhat it gives you
wss://kels-telemetry-hub.sanis-c48.workers.dev/liveThe live stream. Start here — a full snapshot on connect, then changes as they happen.
https://kels-telemetry-hub.sanis-c48.workers.dev/api/latestCurrent values as plain JSON. For tools that would rather poll than hold a socket open.
https://kels-telemetry-hub.sanis-c48.workers.dev/api/signalsThe channel table: id, name, unit, range. Fetch once at startup.
https://kels-telemetry-hub.sanis-c48.workers.dev/api/lapsEvery completed lap of the current session, with sector times. Add ?session=<id> for an earlier one.
https://kels-telemetry-hub.sanis-c48.workers.dev/api/sessionsEvery session we have recorded, newest first, with lap counts.
https://kels-telemetry-hub.sanis-c48.workers.dev/api/configTrack, event name and links for the current session.
https://kels-telemetry-hub.sanis-c48.workers.dev/api/healthWhether the car is currently streaming, and viewer count.

CORS is open on all of them. Current channel table version: 234eb0a69e6f.

Getting started

const socket = new WebSocket("wss://kels-telemetry-hub.sanis-c48.workers.dev/live");

// Signal ids are stable. Fetch the table once to map id -> name/unit/range.
const table = await (await fetch("https://kels-telemetry-hub.sanis-c48.workers.dev/api/signals")).json();
const byId = Object.fromEntries(table.signals.map((s) => [s.id, s]));

const latest = {};

socket.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.t === "snap" || msg.t === "delta") {
    // A delta carries ONLY what changed. Merge, never replace.
    Object.assign(latest, msg.v);

    const speed = latest[table.byName.Vehicle_Speed];
    const rpm = latest[table.byName.Engine_RPM];
    render(speed, rpm);
  }

  if (msg.t === "status" && !msg.car) {
    // The car stopped streaming. Show that -- do not keep painting old numbers.
    showOffline();
  }
};

Messages

snap

Once on connect, and in reply to a resync request.

{ "t": "snap", "seq": 1042, "ts": 1754668800123, "v": { "0": 6820, "1": 98.4 }, "age": { "0": 40, "1": 900 } }

Full state. `age` is milliseconds since each channel last changed.

delta

Ten times a second while the car is running.

{ "t": "delta", "seq": 1043, "ts": 1754668800173, "v": { "0": 6910, "7": 41 } }

Changed channels only. An absent id has not changed — carry the last value forward.

status

On connect and whenever the link changes.

{ "t": "status", "car": true, "since": 1754668790000, "viewers": 312, "carId": "kels-01" }

`car: false` means the car is not transmitting. Surface it rather than showing stale values.

laps

On connect, and again when a new session replaces the table.

{ "t": "laps", "sessionId": 4, "laps": [{ "id": 118, "lap": 7, "time": 91.86, "sectors": [30.44, 31.19, 30.23], "states": [1, 2, 3] }] }

Every completed lap of the current session. Times are seconds; `null` means we could not resolve it and you should not guess either. `states` is 0 incomplete, 1 no improvement, 2 session best, 3 all-time best.

lap

As each lap closes, and two or three times again as it fills in.

{ "t": "lap", "lap": { "id": 118, "lap": 7, "time": 91.86, "sectors": [30.44, 31.19, 30.23], "states": [1, 2, 3] } }

Upsert by `id`, do not append. A lap is published the moment the line is crossed — usually before its time or third sector exist — and republished as they arrive.

config

On connect and whenever the team changes it.

{ "t": "config", "track": "Circuit of the Americas", "event": "Round 4", "links": [{ "label": "Live stream", "url": "https://youtu.be/..." }] }

Where the session is and what it is called, for a lower third or a title card. Set by hand, so treat every field as optional.

Things you can send us

Three things worth knowing

  1. Deltas are partial. A message contains only what changed. Merge it into your copy of the state — do not replace.
  2. Values are already scaled. Speed is km/h, tire temperatures are °C, tire pressures are kPa (not psi), power is kW. Battery power goes negative under regeneration.
  3. Nothing is faked. If the car goes offline you get a status message rather than zeros. A channel that stops updating simply stops appearing in deltas — check its age rather than assuming it is current. The same rule holds in the lap table: a sector we could not resolve is null, never a guess.
  4. Laps are amended after they are published. The lap time and the third sector both arrive a second or two after the line. Key on the lap's id and replace, rather than appending each message as a new row.

Channels

Ids are stable but are regenerated if the car's CAN database changes. Fetch /api/signals at startup rather than hardcoding them; the table below is the current state.

Engine

IdNameDescriptionUnitTypical range
0Engine_RPMRPMrpm0 to 8300
5Engine_PowerEngine PowerkW-50 to 700
22Lambda_Bank_1Lambda B1LA0.7 to 1.2
23Lambda_Bank_2Lambda B2LA0.7 to 1.2

Driver inputs

IdNameDescriptionUnitTypical range
1Driver_PedalThrottle Pedal%0 to 100
2Driver_SteeringSteeringdeg-360 to 360
3Brake_Pres_FBrake Frontbar0 to 100
4Brake_Pres_RBrake Rearbar0 to 100
8Throttle_PositionThrottle Position%0 to 100
17Vehicle_SpeedSpeedkm/h0 to 300
18GearGear—-1 to 8

Hybrid system

IdNameDescriptionUnitTypical range
6Battery_PowerBattery PowerkW-50 to 150
7Battery_VoltageBattery VoltsV250 to 900
65Battery_EnergyState of Charge%0 to 100

Temperatures

IdNameDescriptionUnitTypical range
9Coolant_TempCoolantC60 to 130
10Oil_TempEngine OilC60 to 150
11Battery_TempBatteryC20 to 70
12Motor_TempMotorC20 to 160
13Trans_TempTransmissionC40 to 150
14Diff_TempDifferentialC40 to 160
15Hy_Coolant_TempHybrid CoolantC20 to 80
16Inverter_TempInverterC20 to 100

Pressures

IdNameDescriptionUnitTypical range
19Shift_Tank_PressureShift Tankkpa0 to 1000
20Oil_PressureOil Pressurekpa0 to 800
21Crankcase_PressureCrankcasekpa0 to 150
24Fuel_PressureFuel PressurekPa0 to 700

Warnings

IdNameDescriptionUnitTypical range
25Knock_WarningKnock—0 to 255
26Warning_LightWarning Light—0 to 255
27Warning_SourceWarning Source—0 to 255

lap

IdNameDescriptionUnitTypical range
28Reference_Lap_TimeReference Laps0 to 240
64Split_TimeSplitS0 to 240
66VBOX_LapTime_DispLap Times0 to 240
67Sector_1_StateSector 1—0 to 3
68Sector_2_StateSector 2—0 to 3
69Sector_3_StateSector 3—0 to 3
70Lap_NumberLap—0 to 255
71LastLapTimeLast Laps0 to 240
72SectorSector—0 to 3
73LapGainLossRunningGain / Losss-1.5 to 1.5
74LapTimePredictedPredicted Laps0 to 240

Chassis

IdNameDescriptionUnitTypical range
29ABS_MapABS—0 to 12
30Hybrid_MapHybrid—0 to 12
31Regen_MapRegen—0 to 12
32Traction_MapTraction—0 to 12
33DRS_PositionDRSdeg0 to 40
34Rear_Steer_LeftRear Steer Lmm-10 to 10
35Rear_Steer_RightRear Steer Rmm-10 to 10

Tires

IdNameDescriptionUnitTypical range
36LF_Tire_PresLeft Front Pressurekpa100 to 300
37RF_Tire_PresRight Front Pressurekpa100 to 300
38LR_Tire_PresLeft Rear Pressurekpa100 to 300
39RR_Tire_PresRight Rear Pressurekpa100 to 300
40LF_Tire_Temp_InsideLeft Front Tread InsideC40 to 140
41LF_Tire_Temp_MiddleLeft Front Tread MiddleC40 to 140
42LF_Tire_Temp_OutsideLeft Front Tread OutsideC40 to 140
43RF_Tire_Temp_InsideRight Front Tread InsideC40 to 140
44RF_Tire_Temp_MiddleRight Front Tread MiddleC40 to 140
45RF_Tire_Temp_OutsideRight Front Tread OutsideC40 to 140
46LR_Tire_Temp_InsideLeft Rear Tread InsideC40 to 140
47LR_Tire_Temp_MiddleLeft Rear Tread MiddleC40 to 140
48LR_Tire_Temp_OutsideLeft Rear Tread OutsideC40 to 140
49RR_Tire_Temp_InsideRight Rear Tread InsideC40 to 140
50RR_Tire_Temp_MiddleRight Rear Tread MiddleC40 to 140
51RR_Tire_Temp_OutsideRight Rear Tread OutsideC40 to 140
52LF_Tire_Temp_Carcass_InsideLeft Front Carcass InsideC40 to 140
53LF_Tire_Temp_Carcass_MiddleLeft Front Carcass MiddleC40 to 140
54LF_Tire_Temp_Carcass_OutsideLeft Front Carcass OutsideC40 to 140
55RF_Tire_Temp_Carcass_InsideRight Front Carcass InsideC40 to 140
56RF_Tire_Temp_Carcass_MiddleRight Front Carcass MiddleC40 to 140
57RF_Tire_Temp_Carcass_OutsideRight Front Carcass OutsideC40 to 140
58LR_Tire_Temp_Carcass_InsideLeft Rear Carcass InsideC40 to 140
59LR_Tire_Temp_Carcass_MiddleLeft Rear Carcass MiddleC40 to 140
60LR_Tire_Temp_Carcass_OutsideLeft Rear Carcass OutsideC40 to 140
61RR_Tire_Temp_Carcass_InsideRight Rear Carcass InsideC40 to 140
62RR_Tire_Temp_Carcass_MiddleRight Rear Carcass MiddleC40 to 140
63RR_Tire_Temp_Carcass_OutsideRight Rear Carcass OutsideC40 to 140