📖 OpenLiveTiming API Documentation

Overview

OpenLiveTiming provides a REST API and SignalR hub for streaming live motorsport timing data. You can push race data from your own timing application and have it displayed in real-time on your dedicated live timing page.

The platform is organized around a simple hierarchy:

Account (your organization)
  └─ Instance (a live timing feed, e.g. "adac-mx-masters")
       └─ API Key (grants push access to specific instances)

Authentication

The API supports two authentication methods for pushing data:

API Key (recommended for workers)

Use the X-Api-Key header with a key created from your account dashboard.

X-Api-Key: olt_a1b2c3d4e5f6...
JWT Bearer Token (for user-facing operations)

Obtain a token via POST /api/auth/login:

POST /api/auth/login
Content-Type: application/json

{
  "email": "you@example.com",
  "password": "your-password"
}

// Response:
{
  "token": "eyJhbGciOi...",
  "email": "you@example.com",
  "displayName": "Your Name"
}

// Use in subsequent requests:
Authorization: Bearer eyJhbGciOi...

Quick Start

  1. Create your live timing at /create (or via the API).
  2. Create an API key from your account dashboard, scoped to your instance.
  3. Push a full state to initialize your timing page (see below).
  4. Push incremental events as the race progresses.
  5. View live at openlivetiming.com/lt/your-slug, or embed it on your website.

Pushing Race Data

Full State (complete snapshot)

Push a complete race state. This is the primary way to update your timing page. New viewers and reconnecting clients will receive this state immediately.

POST /api/instances/{slug}/race/fullstate
X-Api-Key: your-api-key
Content-Type: application/json

{
  "session": {
    "trackName": "Circuit de Spa",
    "groupName": "Race 1",
    "runName": "Final",
    "sectorCount": 3,
    "flagStatus": "Green",
    "timeRemaining": "00:25:00",
    "timeOfDay": "14:30:00",
    "totalLaps": 20,
    "currentLap": 5
  },
  "drivers": {
    "1": {
      "number": "1",
      "name": "Max Speed",
      "team": "Team Alpha",
      "position": 1,
      "laps": 5,
      "lastLapTime": "00:01:45.123",
      "bestLapTime": "00:01:44.567",
      "sectorTimes": ["00:00:32.100", "00:00:38.200", "00:00:34.823"],
      "bestSectorTimes": ["00:00:31.900", "00:00:37.800", "00:00:34.567"],
      "gap": "",
      "interval": "",
      "status": "Running"
    },
    "44": {
      "number": "44",
      "name": "Lewis Quick",
      "team": "Team Beta",
      "position": 2,
      "laps": 5,
      "lastLapTime": "00:01:45.456",
      "bestLapTime": "00:01:44.890",
      "gap": "+0.323",
      "interval": "+0.323",
      "status": "Running"
    }
  }
}
Update Cache Only (no broadcast)

Use PUT instead of POST to update the cached state without broadcasting to connected viewers. Useful for high-frequency updates.

PUT /api/instances/{slug}/race/fullstate
X-Api-Key: your-api-key
Content-Type: application/json

{ ...same body... }
Push Individual Events

Send individual race events for real-time updates:

POST /api/instances/{slug}/race/event
X-Api-Key: your-api-key
Content-Type: application/json

{
  "$type": "LapCompleted",
  "number": "44",
  "lapTime": "00:01:44.567",
  "lapNumber": 6,
  "position": 2
}
Push Batch Events
POST /api/instances/{slug}/race/events
X-Api-Key: your-api-key
Content-Type: application/json

[
  { "$type": "SectorCompleted", "number": "1", "sector": 1, "sectorTime": "00:00:31.900" },
  { "$type": "SectorCompleted", "number": "44", "sector": 1, "sectorTime": "00:00:32.100" }
]

Race Event Types

Events use a $type discriminator for polymorphic JSON serialization:

TypeDescription
SectorCompletedA driver completed a timing sector
LapCompletedA driver completed a lap
PositionChangeDriver classification position changed
BestLapNew session overall best lap time
BestSectorNew session overall best sector time
FlagChangeTrack flag status changed (Green, Yellow, Red, etc.)
SessionInfoUpdateSession metadata updated
FullStateComplete state snapshot
AnnouncementRace control announcement

Real-Time via SignalR

For consuming live data in real-time, connect to the SignalR hub:

// JavaScript example
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/racehub?instance=your-slug")
    .build();

// Request the current state
await connection.start();
await connection.invoke("RequestFullState");

// Listen for events
connection.on("ReceiveFullState", (state) => {
    console.log("Full state:", state);
});

connection.on("ReceiveRaceEvent", (event) => {
    console.log("Event:", event);
});

connection.on("ReceiveBatchEvents", (events) => {
    console.log("Batch:", events);
});
Hub Methods
MethodDirectionDescription
RequestFullState()Client → ServerRequest cached full state
ReceiveFullStateServer → ClientComplete race snapshot
ReceiveRaceEventServer → ClientSingle race event
ReceiveBatchEventsServer → ClientBatch of race events

Interactive API Reference

For the complete interactive API reference with request/response schemas, visit the OpenAPI documentation:

OpenAPI Spec (JSON)

Questions? Reach out at info@openlivetiming.com

An unhandled error has occurred. Reload 🗙