Skip to main content

🏎️ OpenF1 Python Client

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

Buy Me A Coffee


📑 Table of Contents


✨ 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:

Buy Me A Coffee

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)

Source distribution for OpenF1-python-client 1.0.3
File Size Uploaded Provenance
openf1_python_client-1.0.3.tar.gz 43.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for OpenF1-python-client 1.0.3
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page