Skip to content

A production-grade Python SDK for the OpenF1 API, providing easy access to real-time and historical Formula 1 data.

License

Notifications You must be signed in to change notification settings

rhtnr/OpenF1-python-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

9 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐ŸŽ๏ธ 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

About

A production-grade Python SDK for the OpenF1 API, providing easy access to real-time and historical Formula 1 data.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages