A production-grade Python SDK for the OpenF1 API, providing easy access to real-time and historical Formula 1 data.
PyPI version PyPI downloads Publish to PyPI Python 3.10+ License: MIT Code style: black Pydantic v2 Ruff
- 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-client
Or 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 tests
This 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