🏎️ OpenF1 Python Client
A production-grade Python SDK for the OpenF1 API, providing easy access to real-time and historical Formula 1 data.
📑 Table of Contents
- Features
- Installation
- Quick Start
- Filtering Data
- Available Endpoints
- Endpoint Methods
- Configuration
- Error Handling
- Logging
- Data Models
- Examples
- Future Enhancements
- Contributing
- License
- Acknowledgements
✨ Features
- 🔌 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
📦 Installation
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]"
🚀 Quick Start
Basic Usage (Unauthenticated)
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()
Using Context Manager
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}")
🔐 Authenticated Usage
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")
🔍 Filtering Data
OpenF1 supports rich filtering with comparison operators. The client provides a Pythonic interface:
Simple Equality
# Filter by exact values
laps = client.laps.list(
session_key=9161,
driver_number=63,
lap_number=8,
)
Comparison Operators
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},
)
Range Filters
# 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},
)
Using FilterBuilder
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)
📡 Available Endpoints
| 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(...) |
🛠️ Endpoint Methods
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)
⚙️ Configuration
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,
)
⚠️ Error Handling
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}")
📝 Logging
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)
📊 Data Models
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")
💡 Examples
🏁 Analyze a Race
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)}")
🌤️ Track Weather Changes
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}%")
🔀 Find Overtakes
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")
🔮 Future Enhancements
The client is designed to be easily extended. Planned additions:
⚡ Async Support
# 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)
📡 Real-time Streaming
# 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")
🤝 Contributing
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
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgements
- 🏎️ 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
Metadata
Release files for OpenF1-python-client 1.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| openf1_python_client-1.0.3.tar.gz | 43.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openf1_python_client-1.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 94.6 kB
Release files / openf1_python_client-1.0.3.tar.gz
| Download URL | openf1_python_client-1.0.3.tar.gz |
|---|---|
| Size | 43.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
251242b5e43f003fb54291b6c18792f38de858f0068aaf881cfa4e2e176a7e9a
|
|
BLAKE2b-256 checksum How to use checksums |
0a6ca7a6559b4967709342cb0f80e71af83747bd2eb06e1c403faa557943ec1d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 1, 2025.
Transparency logRelease files / openf1_python_client-1.0.3-py3-none-any.whl
| Download URL | openf1_python_client-1.0.3-py3-none-any.whl |
|---|---|
| Size | 50.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b02d0182bbcaf4036e8e14f90be0fa109c07f29c756fc29a08d51d5b1a932f0d
|
|
BLAKE2b-256 checksum How to use checksums |
296a57531552f9b2cbb5d5e7ca22b8458a4f423f107039e6e231b71dd487f99d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 1, 2025.
Transparency logSecurity for OpenF1-python-client 1.0.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for openf1_python_client-1.0.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 1, 2025.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.