Introduction
OpenF1 is a free and open-source API that provides real-time and historical Formula 1 data.
The API offers a wealth of information, including lap timings, car telemetry, radio communications, and more. Whether you're looking to create interactive dashboards, dive deep into race analysis, or even develop connected objects that light up every time your favorite driver takes the lead, OpenF1 makes it all possible.
Data can be accessed in either JSON or CSV formats, making it user-friendly for both developers and non-developers alike. For a quick start, you can access the API through your web browser. A sample URL is provided for each method for easy reference.
API methods
Car data
Some data about each car, at a sample rate of about 3.7 Hz.
curl "https://api.openf1.org/v1/car_data?driver_number=55&session_key=9159&speed>=315"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/car_data?driver_number=55&session_key=9159&speed>=315')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/car_data?driver_number=55&session_key=9159&speed>=315')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/car_data?driver_number=55&session_key=9159&speed>=315')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"brake": 0,
"date": "2023-09-15T13:08:19.923000+00:00",
"driver_number": 55,
"drs": 12,
"meeting_key": 1219,
"n_gear": 8,
"rpm": 11141,
"session_key": 9159,
"speed": 315,
"throttle": 99
},
{
"brake": 100,
"date": "2023-09-15T13:35:41.808000+00:00",
"driver_number": 55,
"drs": 8,
"meeting_key": 1219,
"n_gear": 8,
"rpm": 11023,
"session_key": 9159,
"speed": 315,
"throttle": 57
}
]
HTTP Request
GET https://api.openf1.org/v1/car_data
Sample URL
https://api.openf1.org/v1/car_data?driver_number=55&session_key=9159&speed>=315
Attributes
Name | Description |
---|---|
brake | Whether the brake pedal is pressed (100 ) or not (0 ). |
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
drs | The Drag Reduction System (DRS) status (see mapping table below). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
n_gear | Current gear selection, ranging from 1 to 8. 0 indicates neutral or no gear engaged. |
rpm | Revolutions per minute of the engine. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
speed | Velocity of the car in km/h. |
throttle | Percentage of maximum engine power being used. |
Below is a table that correlates DRS values to its supposed interpretation (from FastF1).
DRS value | Interpretation |
---|---|
0 | DRS off |
1 | DRS off |
2 | ? |
3 | ? |
8 | Detected, eligible once in activation zone |
9 | ? |
10 | DRS on |
12 | DRS on |
14 | DRS on |
Drivers
Provides information about drivers for each session.
curl "https://api.openf1.org/v1/drivers?driver_number=1&session_key=9158"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/drivers?driver_number=1&session_key=9158')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/drivers?driver_number=1&session_key=9158')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/drivers?driver_number=1&session_key=9158')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"broadcast_name": "M VERSTAPPEN",
"country_code": "NED",
"driver_number": 1,
"first_name": "Max",
"full_name": "Max VERSTAPPEN",
"headshot_url": "https://www.formula1.com/content/dam/fom-website/drivers/M/MAXVER01_Max_Verstappen/maxver01.png.transform/1col/image.png",
"last_name": "Verstappen",
"meeting_key": 1219,
"name_acronym": "VER",
"session_key": 9158,
"team_colour": "3671C6",
"team_name": "Red Bull Racing"
}
]
HTTP Request
GET https://api.openf1.org/v1/drivers
Sample URL
https://api.openf1.org/v1/drivers?driver_number=1&session_key=9158
Attributes
Name | Description |
---|---|
broadcast_name | The driver's name, as displayed on TV. |
country_code | A code that uniquely identifies the country. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
first_name | The driver's first name. |
full_name | The driver's full name. |
headshot_url | URL of the driver's face photo. |
last_name | The driver's last name. |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
name_acronym | Three-letter acronym of the driver's name. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
team_colour | The hexadecimal color value (RRGGBB) of the driver's team. |
team_name | Name of the driver's team. |
Intervals
Fetches real-time interval data between drivers and their gap to the race leader.
Available during races only, with updates approximately every 4 seconds.
curl "https://api.openf1.org/v1/intervals?session_key=9165&interval<0.005"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/intervals?session_key=9165&interval<0.005')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/intervals?session_key=9165&interval<0.005')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/intervals?session_key=9165&interval<0.005')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"date": "2023-09-17T13:31:02.395000+00:00",
"driver_number": 1,
"gap_to_leader": 41.019,
"interval": 0.003,
"meeting_key": 1219,
"session_key": 9165
}
]
HTTP Request
GET https://api.openf1.org/v1/intervals
Sample URL
https://api.openf1.org/v1/intervals?session_key=9165&interval<0.005
Attributes
Name | Description |
---|---|
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
gap_to_leader | The time gap to the race leader in seconds, +1 LAP if lapped, or null for the race leader. |
interval | The time gap to the car ahead in seconds, +1 LAP if lapped, or null for the race leader. |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
Laps
Provides detailed information about individual laps.
curl "https://api.openf1.org/v1/laps?session_key=9161&driver_number=63&lap_number=8"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/laps?session_key=9161&driver_number=63&lap_number=8')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/laps?session_key=9161&driver_number=63&lap_number=8')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/laps?session_key=9161&driver_number=63&lap_number=8')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"date_start": "2023-09-16T13:59:07.606000+00:00",
"driver_number": 63,
"duration_sector_1": 26.966,
"duration_sector_2": 38.657,
"duration_sector_3": 26.12,
"i1_speed": 307,
"i2_speed": 277,
"is_pit_out_lap": false,
"lap_duration": 91.743,
"lap_number": 8,
"meeting_key": 1219,
"segments_sector_1": [
2049,
2049,
2049,
2051,
2049,
2051,
2049,
2049
],
"segments_sector_2": [
2049,
2049,
2049,
2049,
2049,
2049,
2049,
2049
],
"segments_sector_3": [
2048,
2048,
2048,
2048,
2048,
2064,
2064,
2064
],
"session_key": 9161,
"st_speed": 298
}
]
HTTP Request
GET https://api.openf1.org/v1/laps
Sample URL
https://api.openf1.org/v1/laps?session_key=9161&driver_number=63&lap_number=8
Attributes
Name | Description |
---|---|
date_start | The UTC starting date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
duration_sector_1 | The time taken, in seconds, to complete the first sector of the lap. |
duration_sector_2 | The time taken, in seconds, to complete the second sector of the lap. |
duration_sector_3 | The time taken, in seconds, to complete the third sector of the lap. |
i1_speed | The speed of the car, in km/h, at the first intermediate point on the track. |
i2_speed | The speed of the car, in km/h, at the second intermediate point on the track. |
is_pit_out_lap | A boolean value indicating whether the lap is an "out lap" from the pit (true if it is, false otherwise). |
lap_duration | The total time taken, in seconds, to complete the entire lap. |
lap_number | The sequential number of the lap within the session (starts at 1). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
segments_sector_1 | A list of values representing the "mini-sectors" within the first sector (see mapping table below). |
segments_sector_2 | A list of values representing the "mini-sectors" within the second sector (see mapping table below). |
segments_sector_3 | A list of values representing the "mini-sectors" within the third sector (see mapping table below). |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
st_speed | The speed of the car, in km/h, at the speed trap, which is a specific point on the track where the highest speeds are usually recorded. |
Below is a table that correlates segment values to their meaning.
Value | Color |
---|---|
0 | not available |
2048 | yellow sector |
2049 | green sector |
2050 | ? |
2051 | purple sector |
2052 | ? |
2064 | pitlane |
2068 | ? |
Segments are not available during races. Also, The segment values may not always align perfectly with the colors shown on TV, for unknown reasons.
Location
The approximate location of the cars on the circuit, at a sample rate of about 3.7 Hz.
Useful for gauging their progress along the track, but lacks details about lateral placement — i.e. whether
the car is on the left or right side of the track. The origin point (0, 0, 0) appears to be arbitrary
and not tied to any specific location on the track.
curl "https://api.openf1.org/v1/location?session_key=9161&driver_number=81&date>2023-09-16T13:03:35.200&date<2023-09-16T13:03:35.800"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/location?session_key=9161&driver_number=81&date>2023-09-16T13:03:35.200&date<2023-09-16T13:03:35.800')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/location?session_key=9161&driver_number=81&date>2023-09-16T13:03:35.200&date<2023-09-16T13:03:35.800')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/location?session_key=9161&driver_number=81&date>2023-09-16T13:03:35.200&date<2023-09-16T13:03:35.800')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"date": "2023-09-16T13:03:35.292000+00:00",
"driver_number": 81,
"meeting_key": 1219,
"session_key": 9161,
"x": 567,
"y": 3195,
"z": 187
},
{
"date": "2023-09-16T13:03:35.752000+00:00",
"driver_number": 81,
"meeting_key": 1219,
"session_key": 9161,
"x": 489,
"y": 3403,
"z": 186
}
]
HTTP Request
GET https://api.openf1.org/v1/location
Sample URL
Attributes
Name | Description |
---|---|
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
x | The 'x' value in a 3D Cartesian coordinate system representing the current approximate location of the car on the track. |
y | The 'y' value in a 3D Cartesian coordinate system representing the current approximate location of the car on the track. |
z | The 'z' value in a 3D Cartesian coordinate system representing the current approximate location of the car on the track. |
Meetings
Provides information about meetings.
A meeting refers to a Grand Prix or testing weekend and usually includes multiple sessions (practice, qualifying, race, ...).
curl "https://api.openf1.org/v1/meetings?year=2023&country_name=Singapore"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/meetings?year=2023&country_name=Singapore')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/meetings?year=2023&country_name=Singapore')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/meetings?year=2023&country_name=Singapore')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"circuit_key": 61,
"circuit_short_name": "Singapore",
"country_code": "SGP",
"country_key": 157,
"country_name": "Singapore",
"date_start": "2023-09-15T09:30:00+00:00",
"gmt_offset": "08:00:00",
"location": "Marina Bay",
"meeting_key": 1219,
"meeting_name": "Singapore Grand Prix",
"meeting_official_name": "FORMULA 1 SINGAPORE AIRLINES SINGAPORE GRAND PRIX 2023",
"year": 2023
}
]
HTTP Request
GET https://api.openf1.org/v1/meetings
Sample URL
https://api.openf1.org/v1/meetings?year=2023&country_name=Singapore
Attributes
Name | Description |
---|---|
circuit_key | The unique identifier for the circuit where the event takes place. |
circuit_short_name | The short or common name of the circuit where the event takes place. |
country_code | A code that uniquely identifies the country. |
country_key | The unique identifier for the country where the event takes place. |
country_name | The full name of the country where the event takes place. |
date_start | The UTC starting date and time, in ISO 8601 format. |
gmt_offset | The difference in hours and minutes between local time at the location of the event and Greenwich Mean Time (GMT). |
location | The city or geographical location where the event takes place. |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
meeting_name | The name of the meeting. |
meeting_official_name | The official name of the meeting. |
year | The year the event takes place. |
Pit
Provides information about cars going through the pit lane.
curl "https://api.openf1.org/v1/pit?session_key=9158&pit_duration<31"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/pit?session_key=9158&pit_duration<31')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/pit?session_key=9158&pit_duration<31')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/pit?session_key=9158&pit_duration<31')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"date": "2023-09-15T09:38:23.038000+00:00",
"driver_number": 63,
"lap_number": 5,
"meeting_key": 1219,
"pit_duration": 24.5,
"session_key": 9158
},
{
"date": "2023-09-15T10:05:01.229000+00:00",
"driver_number": 81,
"lap_number": 13,
"meeting_key": 1219,
"pit_duration": 30.8,
"session_key": 9158
}
]
HTTP Request
GET https://api.openf1.org/v1/pit
Sample URL
https://api.openf1.org/v1/pit?session_key=9158&pit_duration<31
Attributes
Name | Description |
---|---|
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
lap_number | The sequential number of the lap within the session (starts at 1). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
pit_duration | The time spent in the pit, from entering to leaving the pit lane, in seconds. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
Position
Provides driver positions throughout a session, including initial placement and subsequent changes.
curl "https://api.openf1.org/v1/position?meeting_key=1217&driver_number=40&position<=3"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/position?meeting_key=1217&driver_number=40&position<=3')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/position?meeting_key=1217&driver_number=40&position<=3')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/position?meeting_key=1217&driver_number=40&position<=3')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"date": "2023-08-26T09:30:47.199000+00:00",
"driver_number": 40,
"meeting_key": 1217,
"position": 2,
"session_key": 9144
},
{
"date": "2023-08-26T09:35:51.477000+00:00",
"driver_number": 40,
"meeting_key": 1217,
"position": 3,
"session_key": 9144
}
]
HTTP Request
GET https://api.openf1.org/v1/position
Sample URL
https://api.openf1.org/v1/position?meeting_key=1217&driver_number=40&position<=3
Attributes
Name | Description |
---|---|
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
position | Position of the driver (starts at 1). |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
Race control
Provides information about race control (racing incidents, flags, safety car, ...).
curl "https://api.openf1.org/v1/race_control?flag=BLACK AND WHITE&driver_number=1&date>=2023-01-01&date<2023-09-01"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/race_control?flag=BLACK AND WHITE&driver_number=1&date>=2023-01-01&date<2023-09-01')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/race_control?flag=BLACK AND WHITE&driver_number=1&date>=2023-01-01&date<2023-09-01')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/race_control?flag=BLACK AND WHITE&driver_number=1&date>=2023-01-01&date<2023-09-01')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"category": "Flag",
"date": "2023-06-04T14:21:01+00:00",
"driver_number": 1,
"flag": "BLACK AND WHITE",
"lap_number": 59,
"meeting_key": 1211,
"message": "BLACK AND WHITE FLAG FOR CAR 1 (VER) - TRACK LIMITS",
"scope": "Driver",
"sector": null,
"session_key": 9102
}
]
HTTP Request
GET https://api.openf1.org/v1/race_control
Sample URL
Attributes
Name | Description |
---|---|
category | The category of the event (CarEvent , Drs , Flag , SafetyCar , ...). |
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
flag | Type of flag displayed (GREEN , YELLOW , DOUBLE YELLOW , CHEQUERED , ...). |
lap_number | The sequential number of the lap within the session (starts at 1). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
message | Description of the event or action. |
scope | The scope of the event (Track , Driver , Sector , ...). |
sector | Segment ("mini-sector") of the track where the event occurred? (starts at 1). |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
Sessions
Provides information about sessions.
A session refers to a distinct period of track activity during a Grand Prix or testing weekend (practice, qualifying, sprint, race, ...).
curl "https://api.openf1.org/v1/sessions?country_name=Belgium&session_name=Sprint&year=2023"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/sessions?country_name=Belgium&session_name=Sprint&year=2023')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/sessions?country_name=Belgium&session_name=Sprint&year=2023')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/sessions?country_name=Belgium&session_name=Sprint&year=2023')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"circuit_key": 7,
"circuit_short_name": "Spa-Francorchamps",
"country_code": "BEL",
"country_key": 16,
"country_name": "Belgium",
"date_end": "2023-07-29T15:35:00+00:00",
"date_start": "2023-07-29T15:05:00+00:00",
"gmt_offset": "02:00:00",
"location": "Spa-Francorchamps",
"meeting_key": 1216,
"session_key": 9140,
"session_name": "Sprint",
"session_type": "Race",
"year": 2023
}
]
HTTP Request
GET https://api.openf1.org/v1/sessions
Sample URL
https://api.openf1.org/v1/sessions?country_name=Belgium&session_name=Sprint&year=2023
Attributes
Name | Description |
---|---|
circuit_key | The unique identifier for the circuit where the event takes place. |
circuit_short_name | The short or common name of the circuit where the event takes place. |
country_code | A code that uniquely identifies the country. |
country_key | The unique identifier for the country where the event takes place. |
country_name | The full name of the country where the event takes place. |
date_end | The UTC ending date and time, in ISO 8601 format. |
date_start | The UTC starting date and time, in ISO 8601 format. |
gmt_offset | The difference in hours and minutes between local time at the location of the event and Greenwich Mean Time (GMT). |
location | The city or geographical location where the event takes place. |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
session_name | The name of the session (Practice 1 , Qualifying , Race , ...). |
session_type | The type of the session (Practice , Qualifying , Race , ...). |
year | The year the event takes place. |
Stints
Provides information about individual stints.
A stint refers to a period of continuous driving by a driver during a session.
curl "https://api.openf1.org/v1/stints?session_key=9165&tyre_age_at_start>=3"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/stints?session_key=9165&tyre_age_at_start>=3')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/stints?session_key=9165&tyre_age_at_start>=3')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/stints?session_key=9165&tyre_age_at_start>=3')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"compound": "SOFT",
"driver_number": 16,
"lap_end": 20,
"lap_start": 1,
"meeting_key": 1219,
"session_key": 9165,
"stint_number": 1,
"tyre_age_at_start": 3
},
{
"compound": "SOFT",
"driver_number": 20,
"lap_end": 62,
"lap_start": 44,
"meeting_key": 1219,
"session_key": 9165,
"stint_number": 3,
"tyre_age_at_start": 3
}
]
HTTP Request
GET https://api.openf1.org/v1/stints
Sample URL
https://api.openf1.org/v1/stints?session_key=9165&tyre_age_at_start>=3
Attributes
Name | Description |
---|---|
compound | The specific compound of tyre used during the stint (SOFT , MEDIUM , HARD , ...). |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
lap_end | Number of the last completed lap in this stint. |
lap_start | Number of the initial lap in this stint (starts at 1). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
stint_number | The sequential number of the stint within the session (starts at 1). |
tyre_age_at_start | The age of the tyres at the start of the stint, in laps completed. |
Team radio
Provides a collection of radio exchanges between Formula 1 drivers and their respective teams during sessions.
Please note that only a limited selection of communications are included, not the complete record of radio interactions.
curl "https://api.openf1.org/v1/team_radio?session_key=9158&driver_number=11"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/team_radio?session_key=9158&driver_number=11')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/team_radio?session_key=9158&driver_number=11')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/team_radio?session_key=9158&driver_number=11')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"date": "2023-09-15T09:40:43.005000",
"driver_number": 11,
"meeting_key": 1219,
"recording_url": "https://livetiming.formula1.com/static/2023/2023-09-17_Singapore_Grand_Prix/2023-09-15_Practice_1/TeamRadio/SERPER01_11_20230915_104008.mp3",
"session_key": 9158
},
{
"date": "2023-09-15T10:32:47.325000",
"driver_number": 11,
"meeting_key": 1219,
"recording_url": "https://livetiming.formula1.com/static/2023/2023-09-17_Singapore_Grand_Prix/2023-09-15_Practice_1/TeamRadio/SERPER01_11_20230915_113201.mp3",
"session_key": 9158
}
]
HTTP Request
GET https://api.openf1.org/v1/team_radio
Sample URL
https://api.openf1.org/v1/team_radio?session_key=9158&driver_number=11
Attributes
Name | Description |
---|---|
date | The UTC date and time, in ISO 8601 format. |
driver_number | The unique number assigned to an F1 driver (cf. Wikipedia). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
recording_url | URL of the radio recording. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
Weather
The weather over the track, updated every minute.
curl "https://api.openf1.org/v1/weather?meeting_key=1208&wind_direction>=130&track_temperature>=52"
from urllib.request import urlopen
import json
response = urlopen('https://api.openf1.org/v1/weather?meeting_key=1208&wind_direction>=130&track_temperature>=52')
data = json.loads(response.read().decode('utf-8'))
print(data)
# If you want, you can import the results in a DataFrame (you need to install the `pandas` package first)
# import pandas as pd
# df = pd.DataFrame(data)
# If needed, install libraries
# install.packages('httr')
# install.packages('jsonlite')
library(httr)
library(jsonlite)
response <- GET('https://api.openf1.org/v1/weather?meeting_key=1208&wind_direction>=130&track_temperature>=52')
parsed_data <- fromJSON(content(response, 'text'))
print(parsed_data)
# If you want, you can import the results in a DataFrame
# df <- do.call(rbind, lapply(parsed_data, data.frame, stringsAsFactors = FALSE))
# df <- as.data.frame(t(as.matrix(df)))
fetch('https://api.openf1.org/v1/weather?meeting_key=1208&wind_direction>=130&track_temperature>=52')
.then(response => response.json())
.then(jsonContent => console.log(jsonContent));
Output:
[
{
"air_temperature": 27.8,
"date": "2023-05-07T18:42:25.233000+00:00",
"humidity": 58,
"meeting_key": 1208,
"pressure": 1018.7,
"rainfall": 0,
"session_key": 9078,
"track_temperature": 52.5,
"wind_direction": 136,
"wind_speed": 2.4
}
]
HTTP Request
GET https://api.openf1.org/v1/weather
Sample URL
https://api.openf1.org/v1/weather?meeting_key=1208&wind_direction>=130&track_temperature>=52
Attributes
Name | Description |
---|---|
air_temperature | Air temperature (°C). |
date | The UTC date and time, in ISO 8601 format. |
humidity | Relative humidity (%). |
meeting_key | The unique identifier for the meeting. Use latest to identify the latest or current meeting. |
pressure | Air pressure (mbar). |
rainfall | Whether there is rainfall. |
session_key | The unique identifier for the session. Use latest to identify the latest or current session. |
track_temperature | Track temperature (°C). |
wind_direction | Wind direction (°), from 0° to 359°. |
wind_speed | Wind speed (m/s). |
Data filtering
Refine your query by including parameters directly in the URL.
Results can be filtered by any attribute, except arrays.
Example
To fetch pit-out laps for driver number 55 (Carlos Sainz) that last at least 2 minutes, use:
https://api.openf1.org/v1/laps?session_key=9222&driver_number=55&is_pit_out_lap=true&lap_duration>=120
Time-Based Filtering
You can narrow down your results using time ranges.
Example
To get all sessions in September 2023, use:
https://api.openf1.org/v1/sessions?date_start>=2023-09-01&date_end<=2023-09-30
The API supports a wide range of date formats (those compatible with Python's dateutil.parser.parse
method). Examples include:
- "2021-09-10"
- "2021-09-10T14:30:20"
- "2021-09-10T14:30:20+00:00"
- "09/10/2021"
- "09-10-2021"
- "Fri Sep 10 14:30:20 2021"
- "10 September 2021"
- "Sep 10, 2021"
- "2021-09-10 14:30:20 UTC"
- "2021-09-10 14:30:20+00:00"
- "2021-09-10 14:30:20 EST"
- ...and many more.
CSV Format
To receive your query results in CSV format instead of the default JSON, simply append the query parameter csv=true
to your URL. This feature is particularly handy to import the data into spreadsheet software like Microsoft Excel.
Example
To get all sessions for the year 2023 in CSV format, use the following URL:
https://api.openf1.org/v1/sessions?year=2023&csv=true
System architecture
Our architecture is serverless, optimized for resilience, cost-efficiency and scalability.
Ingestor
The data ingestor runs every 5 minutes to check for new real-time data. During live sessions, at least two instances are active at any time to ensure reliable data recording. The ingestor also performs basic data normalization for efficient database indexing.
Database
We use a MongoDB database with indexes for each collection, enabling faster query responses.
Query API
The Query API serves as the intermediary between the user and the database. It processes user queries, fetches the relevant data from the database, and returns it in the requested format.
FAQ
Can I access past data during an ongoing session?
Yes, real-time storage allows for instant access to past and current session data.
What's the delay between live events and API updates?
The API typically updates about 3 seconds after a live event.
However, this time could be extended due to factors like serverless cold starts, which occur when the service is scaling up.
As a point of reference, F1 TV typically has a 6-second delay.
Is there a query timeout?
Queries are limited to a 1-minute timeout. If your request takes too long, consider breaking it down into smaller queries and then combining the results.
Community and Support
For community-driven questions or feedback, we encourage you to participate in our Github Discussions. If you encounter any bugs or issues, please report them by creating a new issue on our Github repository. We're always open to contributions that enhance the project!
For specialized inquiries, feel free to reach out to me directly. However, for general support questions, please use the Github Discussions platform to ensure that the entire community can benefit from the answers.
Additionally, I offer freelance data science and data engineering services. Whether your project is in the realm of motorsports or spans other domains, you can count on me for high-quality, efficient coding solutions. If you're interested in collaborating, please contact me here.
Roadmap
- Race and championship standings, schedules, and starting grids
- Add data from previous seasons (2018-2022)
- AI radio transcriptions
- More precise pit-stop duration
- Overtakes, undercuts/overcuts data, tyre statistics...
We welcome your ideas and suggestions for OpenF1's future. Please share them on our Github Discussions page.
Usage Guidelines
The OpenF1 API is an open-source service designed to be easily accessible, requiring no authentication and imposing no rate limits. We kindly ask that you use the service responsibly to conserve computational resources. If you find the API useful, please consider donating to support the long-term sustainability of the project.
The creators of OpenF1 disclaim any liability for losses or damages incurred through the use of this API. We cannot guarantee the API's continuous availability or the accuracy of its data.
Acknowledgments
Special thanks to Philipp Schaefer (theOehrly) for his contributions to the FastF1 Python package, which has greatly inspired OpenF1. The SignalR data recorder used in OpenF1 is also sourced from the FastF1 package.