This document provides detailed information about integrating with the OpenF1 API, including endpoints, data structures, filtering, and best practices.
https://api.openf1.org/v1
- Historical Data: No authentication required (free)
- Real-time Data: Requires paid account (not implemented yet)
- No explicit rate limits documented
- Implement client-side caching to minimize requests
- Use React Query for automatic request deduplication
- Query timeout: 10 seconds
- Break large queries into smaller chunks if needed
Purpose: List available race sessions
Endpoint: GET /v1/sessions
Sample Request:
GET https://api.openf1.org/v1/sessions?year=2024&session_name=Race
Query Parameters:
| Parameter | Type | Description | Example |
|---|---|---|---|
session_key |
number | Unique session identifier | 9161 |
session_name |
string | Session type | Race, Qualifying, Practice 1 |
year |
number | Season year | 2024 |
circuit_short_name |
string | Circuit abbreviation | Monaco, Silverstone |
date_start |
string | Start date filter | 2024-05-26 |
Response Structure:
interface Session {
session_key: number
session_name: string
date_start: string // ISO 8601
date_end: string // ISO 8601
gmt_offset: string
session_type: string
meeting_key: number
location: string
country_name: string
circuit_key: number
circuit_short_name: string
year: number
}Example Response:
[
{
"session_key": 9161,
"session_name": "Race",
"date_start": "2023-09-16T13:00:00",
"date_end": "2023-09-16T15:00:00",
"gmt_offset": "+02:00",
"session_type": "Race",
"meeting_key": 1217,
"location": "Singapore",
"country_name": "Singapore",
"circuit_key": 61,
"circuit_short_name": "Singapore",
"year": 2023
}
]Purpose: GPS coordinates of cars on track
Endpoint: GET /v1/location
Sample Request:
GET https://api.openf1.org/v1/location?session_key=9161&driver_number=1&date>2023-09-16T13:00:00
Query Parameters:
| Parameter | Type | Description | Example |
|---|---|---|---|
session_key |
number | Session identifier | 9161 |
driver_number |
number | Driver number | 1, 44, 16 |
date |
string | Timestamp filter | 2023-09-16T13:00:00 |
date> |
string | After timestamp | 2023-09-16T13:00:00 |
date< |
string | Before timestamp | 2023-09-16T14:00:00 |
Response Structure:
interface LocationData {
date: string // ISO 8601 timestamp
driver_number: number // Driver number
meeting_key: number
session_key: number
x: number // Track coordinate X
y: number // Track coordinate Y
z: number // Track coordinate Z (elevation)
lat: number // Latitude
lng: number // Longitude
}Usage Notes:
- Use
date>anddate<for time range queries - Coordinates update approximately every 0.1 seconds
- Use
x,y,zfor track-relative positioning - Use
lat,lngfor GPS mapping
Purpose: Race position of drivers
Endpoint: GET /v1/position
Sample Request:
GET https://api.openf1.org/v1/position?session_key=9161&date>2023-09-16T13:00:00
Query Parameters:
| Parameter | Type | Description | Example |
|---|---|---|---|
session_key |
number | Session identifier | 9161 |
driver_number |
number | Driver number | 1 |
position |
number | Race position | 1, 2, 3 |
position<= |
number | Position less than or equal | 3 |
date |
string | Timestamp filter | 2023-09-16T13:00:00 |
Response Structure:
interface PositionData {
date: string
driver_number: number
meeting_key: number
session_key: number
position: number
}Purpose: Lap times and lap numbers
Endpoint: GET /v1/laps
Sample Request:
GET https://api.openf1.org/v1/laps?session_key=9161&driver_number=1
Query Parameters:
| Parameter | Type | Description | Example |
|---|---|---|---|
session_key |
number | Session identifier | 9161 |
driver_number |
number | Driver number | 1 |
lap_number |
number | Specific lap | 10 |
Response Structure:
interface LapData {
date_start: string
driver_number: number
duration_sector_1: number // seconds
duration_sector_2: number // seconds
duration_sector_3: number // seconds
i1_speed: number // km/h
i2_speed: number // km/h
is_pit_out_lap: boolean
lap_duration: number // seconds
lap_number: number
meeting_key: number
session_key: number
st_speed: number // km/h (speed trap)
}Purpose: Time gaps between drivers
Endpoint: GET /v1/intervals
Sample Request:
GET https://api.openf1.org/v1/intervals?session_key=9161&driver_number=1
Response Structure:
interface IntervalData {
date: string
driver_number: number
gap_to_leader: number // seconds
interval: number // seconds to car ahead
meeting_key: number
session_key: number
}Purpose: Pit stop information
Endpoint: GET /v1/pit
Sample Request:
GET https://api.openf1.org/v1/pit?session_key=9161
Response Structure:
interface PitData {
date: string
driver_number: number
lap_number: number
pit_duration: number // seconds
meeting_key: number
session_key: number
}Purpose: Real-time car telemetry
Endpoint: GET /v1/car_data
Sample Request:
GET https://api.openf1.org/v1/car_data?session_key=9161&driver_number=1&date>2023-09-16T13:00:00
Response Structure:
interface CarData {
brake: number // 0-100 (percentage)
date: string
driver_number: number
drs: number // 0-14 (DRS status)
meeting_key: number
n_gear: number // 1-8
rpm: number // Engine RPM
session_key: number
speed: number // km/h
throttle: number // 0-100 (percentage)
}DRS Values:
0= Off10-14= Various DRS states (available, enabled, etc.)
Purpose: Tire strategy and stints
Endpoint: GET /v1/stints
Sample Request:
GET https://api.openf1.org/v1/stints?session_key=9161&driver_number=1
Response Structure:
interface StintData {
compound: string // "SOFT", "MEDIUM", "HARD", "INTERMEDIATE", "WET"
driver_number: number
lap_end: number
lap_start: number
meeting_key: number
session_key: number
stint_number: number
tyre_age_at_start: number // laps
}OpenF1 supports comparison operators for date filtering:
// After a specific time
date>2023-09-16T13:00:00
// Before a specific time
date<2023-09-16T14:00:00
// Between two times
date>2023-09-16T13:00:00&date<2023-09-16T14:00:00
// Exact time
date=2023-09-16T13:00:00// Position filtering
position<=3 // Top 3 positions
position=1 // Leader only
driver_number=44 // Specific driverUse latest parameter to get most recent data point:
GET /v1/location?session_key=9161&driver_number=1&latest
// React Query configuration
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false,
retry: 1,
},
},
})Instead of:
// ❌ Bad - Multiple requests
const driver1 = await getLocation(sessionKey, 1)
const driver2 = await getLocation(sessionKey, 2)
const driver3 = await getLocation(sessionKey, 3)Do:
// ✅ Good - Single request
const allDrivers = await getLocation(sessionKey)// ✅ Good - Specific time range
const data = await getLocationData(sessionKey, {
'date>': '2023-09-16T13:00:00',
'date<': '2023-09-16T13:01:00',
})try {
const data = await getLocationData(sessionKey)
return data
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response?.status === 404) {
console.error('Session not found')
} else if (error.response?.status === 500) {
console.error('API server error')
} else if (error.code === 'ECONNABORTED') {
console.error('Request timeout')
}
}
throw error
}// For large time ranges, split into chunks
const startTime = new Date('2023-09-16T13:00:00')
const endTime = new Date('2023-09-16T15:00:00')
const chunkSize = 5 * 60 * 1000 // 5 minutes
const chunks = []
for (let time = startTime; time < endTime; time += chunkSize) {
const chunkEnd = new Date(time.getTime() + chunkSize)
chunks.push({
start: time.toISOString(),
end: chunkEnd.toISOString(),
})
}
const allData = await Promise.all(
chunks.map(chunk =>
getLocationData(sessionKey, {
'date>': chunk.start,
'date<': chunk.end,
})
)
)// services/openf1/client.ts
import axios from 'axios'
export const apiClient = axios.create({
baseURL: 'https://api.openf1.org/v1',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
})
// services/openf1/location.ts
import { apiClient } from './client'
import type { LocationData } from '@/types/openf1'
export const getLocationData = async (
sessionKey: number,
filters?: Record<string, string | number>
): Promise<LocationData[]> => {
const response = await apiClient.get<LocationData[]>('/location', {
params: {
session_key: sessionKey,
...filters,
},
})
return response.data
}
// Custom hook
import { useQuery } from '@tanstack/react-query'
export const useLocationData = (
sessionKey: number | null,
filters?: Record<string, string | number>
) => {
return useQuery({
queryKey: ['location', sessionKey, filters],
queryFn: () => sessionKey ? getLocationData(sessionKey, filters) : null,
enabled: !!sessionKey,
refetchInterval: 1000, // Refetch every second for "live" updates
})
}Use these session keys for testing:
9161- Singapore GP 2023 Race9158- Singapore GP 2023 Qualifying9165- Japan GP 2023 Race
# Get session info
curl "https://api.openf1.org/v1/sessions?session_key=9161"
# Get location data for driver 1
curl "https://api.openf1.org/v1/location?session_key=9161&driver_number=1&date>2023-09-16T13:00:00&date<2023-09-16T13:01:00"
# Get race positions
curl "https://api.openf1.org/v1/position?session_key=9161"
# Get telemetry
curl "https://api.openf1.org/v1/car_data?session_key=9161&driver_number=1&date>2023-09-16T13:00:00&date<2023-09-16T13:01:00"Solution: Break large queries into smaller time chunks
Solution: Use time-based filtering and pagination
Solution: Handle null/undefined gracefully, interpolate if needed
Solution: Use provided x, y, z coordinates instead of lat, lng for track visualization
- OpenF1 Official Documentation
- OpenF1 GitHub
- API Status Page (check for downtime)