A production-grade Python SDK for the OpenF1 API, providing easy access to real-time and historical Formula 1 data.
- Features
- Installation
- Quick Start
- Filtering Data
- Available Endpoints
- Endpoint Methods
- Configuration
- Error Handling
- Logging
- Data Models
- Examples
- Future Enhancements
- Contributing
- License
- Acknowledgements
- ๐ Full API Coverage โ Access all 16 OpenF1 endpoints including telemetry, lap times, positions, weather, and more
- ๐ Type Safety โ Fully typed with Pydantic v2 models and comprehensive type hints
- ๐ฏ Pythonic Filtering โ Use dictionaries with comparison operators for flexible queries
- ๐ Authentication Support โ OAuth2 password flow for real-time data access
โ ๏ธ Robust Error Handling โ Comprehensive exception hierarchy with detailed error information- ๐ Production Ready โ Automatic retries, configurable timeouts, and logging support
- ๐ Multiple Formats โ JSON and CSV response support
pip install OpenF1-python-clientOr install from source:
git clone https://github.com/rhtnr/OpenF1-python-client.git
cd OpenF1-python-client
pip install -e .For development:
pip install -e ".[dev]"Historical data is available without authentication:
from openf1_client import OpenF1Client
# Create a client
client = OpenF1Client()
# Get lap data for a specific driver
laps = client.laps.list(
session_key=9161,
driver_number=63,
)
for lap in laps:
print(f"Lap {lap.lap_number}: {lap.lap_duration}s")
# Don't forget to close the client when done
client.close()from openf1_client import OpenF1Client
with OpenF1Client() as client:
# Get driver information
drivers = client.drivers.list(session_key=9158)
for driver in drivers:
print(f"{driver.name_acronym}: {driver.full_name} - {driver.team_name}")For real-time data and higher rate limits, authenticate with your OpenF1 credentials:
from openf1_client import OpenF1Client
client = OpenF1Client(
username="your_email@example.com",
password="your_password",
)
# Access real-time data
latest_session = client.sessions.first(session_key="latest")
print(f"Current session: {latest_session.session_name}")Or use a pre-existing access token:
client = OpenF1Client(access_token="your_access_token")OpenF1 supports rich filtering with comparison operators. The client provides a Pythonic interface:
# Filter by exact values
laps = client.laps.list(
session_key=9161,
driver_number=63,
lap_number=8,
)Use dictionaries with operator keys for comparisons:
# Speed >= 315 km/h
fast_telemetry = client.car_data.list(
session_key=9159,
driver_number=55,
speed={">=": 315},
)
# Close intervals (< 0.5 seconds)
close_battles = client.intervals.list(
session_key=9161,
interval={"<": 0.5},
)# Date range
location_data = client.location.list(
session_key=9161,
driver_number=81,
date={
">": "2023-09-16T13:03:35.200",
"<": "2023-09-16T13:03:35.800",
},
)
# Lap range
stint_laps = client.laps.list(
session_key=9161,
driver_number=1,
lap_number={">=": 10, "<=": 20},
)For more complex filters, use the FilterBuilder helper:
from openf1_client import OpenF1Client, FilterBuilder
with OpenF1Client() as client:
filters = (
FilterBuilder()
.eq("session_key", 9161)
.eq("driver_number", 1)
.gte("speed", 300)
.lt("lap_number", 10)
.build()
)
car_data = client.car_data.list(**filters)| Endpoint | Description | Example |
|---|---|---|
๐๏ธ car_data |
Car telemetry (~3.7 Hz) | client.car_data.list(...) |
๐ค drivers |
Driver information | client.drivers.list(...) |
โฑ๏ธ intervals |
Gap data (~4s updates) | client.intervals.list(...) |
๐ laps |
Lap timing data | client.laps.list(...) |
๐ location |
Car positions (~3.7 Hz) | client.location.list(...) |
๐ meetings |
Grand Prix metadata | client.meetings.list(...) |
๐ overtakes |
Passing events (beta) | client.overtakes.list(...) |
๐ pit |
Pit stop activity | client.pit.list(...) |
๐ position |
Track positions | client.position.list(...) |
๐ฉ race_control |
Flags, incidents | client.race_control.list(...) |
๐
sessions |
Session data | client.sessions.list(...) |
๐ session_result |
Final results (beta) | client.session_result.list(...) |
๐ฆ starting_grid |
Grid positions (beta) | client.starting_grid.list(...) |
๐ง stints |
Stint/tyre data | client.stints.list(...) |
๐ป team_radio |
Radio communications | client.team_radio.list(...) |
๐ค๏ธ weather |
Weather data (~1 min) | client.weather.list(...) |
Each endpoint provides several methods:
# List all matching records
laps = client.laps.list(session_key=9161, driver_number=1)
# Get first matching record (or None)
lap = client.laps.first(session_key=9161, driver_number=1, lap_number=1)
# Get raw data (dict) without model parsing
raw_data = client.laps.list_raw(session_key=9161)
# Get CSV format
csv_data = client.laps.list_csv(session_key=9161)
# Count matching records
count = client.laps.count(session_key=9161, driver_number=1)Many endpoints also provide convenience methods:
# ๐ Laps
fastest_lap = client.laps.get_fastest_lap(session_key=9161)
flying_laps = client.laps.get_flying_laps(session_key=9161, driver_number=1)
# ๐
Sessions
races = client.sessions.get_races(year=2023)
latest = client.sessions.get_latest()
# ๐ง Stints
strategy = client.stints.get_tyre_strategy(session_key=9161, driver_number=1)
# ๐ค๏ธ Weather
rain = client.weather.get_rain_periods(session_key=9161)from openf1_client import OpenF1Client
client = OpenF1Client(
# ๐ Authentication
username="user@example.com",
password="secret",
# Or use a token directly
# access_token="your_token",
# ๐ Connection settings
timeout=60.0, # Request timeout in seconds
# timeout=(5.0, 30.0), # (connect, read) timeouts
max_retries=5, # Retry failed requests
# ๐ Response format
default_format="json", # "json" or "csv"
# ๐ SSL/TLS
verify_ssl=True,
)The client provides a comprehensive exception hierarchy:
from openf1_client import (
OpenF1Client,
OpenF1Error, # Base exception
OpenF1ConfigError, # Invalid configuration
OpenF1TransportError, # Network errors
OpenF1APIError, # API errors (non-2xx)
OpenF1AuthError, # 401/403 errors
OpenF1RateLimitError, # 429 errors
OpenF1NotFoundError, # 404 errors
OpenF1ServerError, # 5xx errors
OpenF1TimeoutError, # Request timeout
OpenF1ValidationError, # Data validation
)
try:
with OpenF1Client() as client:
laps = client.laps.list(session_key=99999)
except OpenF1NotFoundError as e:
print(f"โ Session not found: {e}")
except OpenF1RateLimitError as e:
print(f"โณ Rate limited. Retry after: {e.retry_after}s")
except OpenF1APIError as e:
print(f"โ ๏ธ API error {e.status_code}: {e.message}")
except OpenF1Error as e:
print(f"๐ฅ Client error: {e}")Enable debug logging to see HTTP requests and responses:
from openf1_client import setup_logging
import logging
# Enable debug logging
setup_logging(logging.DEBUG)
# Or configure manually
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("openf1_client")
logger.setLevel(logging.DEBUG)All responses are parsed into Pydantic models with full type annotations:
from openf1_client import OpenF1Client, Lap, Driver
with OpenF1Client() as client:
lap: Lap = client.laps.first(session_key=9161, driver_number=1, lap_number=1)
if lap:
print(f"Lap duration: {lap.lap_duration}")
print(f"Sector 1: {lap.duration_sector_1}")
print(f"Sector 2: {lap.duration_sector_2}")
print(f"Sector 3: {lap.duration_sector_3}")
print(f"Speed trap: {lap.st_speed} km/h")from openf1_client import OpenF1Client
with OpenF1Client() as client:
# Get session info
session = client.sessions.first(session_key=9161)
print(f"๐ Session: {session.session_name} - {session.country_name}")
# Get all drivers
drivers = client.drivers.list(session_key=9161)
for driver in drivers:
# Get their fastest lap
fastest = client.laps.get_fastest_lap(
session_key=9161,
driver_number=driver.driver_number,
)
# Get pit stops
pit_count = client.pit.count_pit_stops(
session_key=9161,
driver_number=driver.driver_number,
)
# Get tyre strategy
strategy = client.stints.get_tyre_strategy(
session_key=9161,
driver_number=driver.driver_number,
)
print(f"๐๏ธ {driver.name_acronym}: "
f"Fastest: {fastest.lap_duration if fastest else 'N/A'}s, "
f"Stops: {pit_count}, "
f"Tyres: {' โ '.join(strategy)}")from openf1_client import OpenF1Client
with OpenF1Client() as client:
weather_data = client.weather.list(session_key=9161)
for w in weather_data:
rain_emoji = "๐ง๏ธ" if w.rainfall else "โ๏ธ"
print(f"{rain_emoji} {w.date}")
print(f" ๐ก๏ธ Air: {w.air_temperature}ยฐC")
print(f" ๐ฃ๏ธ Track: {w.track_temperature}ยฐC")
print(f" ๐ง Humidity: {w.humidity}%")from openf1_client import OpenF1Client
from collections import Counter
with OpenF1Client() as client:
overtakes = client.overtakes.list(session_key=9161)
print(f"๐ Total overtakes: {len(overtakes)}")
# Get drivers with most overtakes
overtake_counts = Counter(o.driver_number for o in overtakes)
print("\n๐ Top overtakers:")
for driver_num, count in overtake_counts.most_common(5):
driver = client.drivers.first(
session_key=9161,
driver_number=driver_num,
)
print(f" {driver.name_acronym}: {count} overtakes")The client is designed to be easily extended. Planned additions:
# Future async client (design ready, not yet implemented)
from openf1_client import AsyncOpenF1Client
async with AsyncOpenF1Client() as client:
laps = await client.laps.list(session_key=9161)# Future streaming support (design ready)
# Via MQTT or WebSockets for live data
async for telemetry in client.car_data.stream(driver_number=1):
print(f"๐๏ธ Speed: {telemetry.speed} km/h")Contributions are welcome! Please read our Contributing Guide for details.
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run type checking
mypy src/openf1_client
# Format code
black src tests
# Lint code
ruff check src testsThis project is licensed under the MIT License - see the LICENSE file for details.
- ๐๏ธ OpenF1 for providing the excellent Formula 1 data API
- โค๏ธ The Formula 1 community for their passion and support
If you find this project useful, consider supporting its development:
Made with โค๏ธ for the F1 community