Skip to main content
The core module is the heart of tif1, providing the primary interface for loading and working with Formula 1 data. All data access flows through the Session object, which serves as the central hub for accessing laps, telemetry, weather, race control messages, and driver information. The API is designed for maximum performance with async support, intelligent caching, and lazy loading patterns.

Overview

The core API is built around a hierarchical data model that mirrors the structure of Formula 1 sessions:
Session (2024 Monaco Grand Prix - Race)
├── Laps (all drivers, all laps)
├── Drivers (VER, HAM, LEC, ...)
│   └── Driver (VER)
│       ├── Laps (VER's laps only)
│       └── Lap (VER lap 19)
│           └── Telemetry (high-frequency data)
├── Weather (temperature, humidity, wind)
├── RaceControlMessages (flags, safety car)
├── Results (final classification)
└── CircuitInfo (corners, track layout)
Key Design Principles:
  • Lazy Loading: Data is fetched only when accessed, minimizing unnecessary network requests. Properties like session.laps, session.weather, and lap.telemetry trigger data loading on first access.
  • Intelligent Caching: Multi-layer caching strategy with in-memory LRU cache (instant access) and SQLite persistent cache (survives restarts). Cache keys are session-specific and backend-aware (pandas vs polars).
  • Async Support: All data-intensive operations have async variants (laps_async(), get_fastest_laps_tels_async()) that use parallel HTTP requests for 4-5x faster loading compared to sequential access.
  • Backend Flexibility: Choose between pandas (default, maximum compatibility) or polars (faster for large datasets, 10-100x speedup for some operations) for DataFrame operations. Backend selection is per-session and affects all data structures.
  • FastF1 Compatibility: Drop-in replacement for FastF1 with the same API surface. All FastF1 methods (pick_driver(), pick_fastest(), iterlaps(), etc.) are supported with identical behavior.
  • Ultra-Cold Mode: Special optimization mode that bypasses cache validation and skips loading unnecessary data for minimal latency. Ideal for serverless environments or when you only need specific data (e.g., fastest lap telemetry).
  • Error Resilience: Comprehensive exception hierarchy (DataNotFoundError, NetworkError, InvalidDataError) with structured error context. Failed telemetry fetches are tracked per-driver to avoid repeated failures.
Performance Characteristics:
OperationCold CacheWarm CacheNotes
session.laps2-4s<100msAuto-async parallel loading
laps_async()0.5-1s<100msExplicit async, 4-5x faster
get_fastest_lap_tel()200-500ms<50msSingle telemetry fetch
get_fastest_laps_tels()300-800ms<100msParallel telemetry for multiple drivers
ultra_cold=True100-300msN/ASkips cache validation
Memory Footprint:
  • Session metadata: ~10-50 KB
  • Laps DataFrame (20 drivers, 60 laps): ~500 KB - 2 MB
  • Single lap telemetry: ~50-200 KB (varies by track length)
  • Full session telemetry (all laps): ~50-200 MB (rarely needed)
Thread Safety:
  • Session objects are thread-safe for read operations
  • Internal caches use threading locks for concurrent access
  • Async methods can be called from multiple coroutines safely

get_session

The main entry point for loading F1 session data. This function handles event name resolution, session validation, and returns a configured Session object ready to load data.
def get_session(
    year: int,
    gp: str | int,
    session: str | int,
    enable_cache: bool | None = None,
    lib: Literal["pandas", "polars"] | None = None
) -> Session
Parameters:
year
int
required
Season year (2018-current). Must be within the supported range.Valid range: 2018 to current seasonExample: 2024, 2025
gp
str | int
required
Grand Prix identifier. Can be specified in multiple ways:
  • Full name: "Monaco Grand Prix", "British Grand Prix"
  • Abbreviated name: "Monaco", "Silverstone" (uses fuzzy matching)
  • Round number: 6, 12 (1-indexed, based on calendar order)
The function automatically resolves abbreviated names and round numbers to the official event name.Example: "Monaco Grand Prix", "Monaco", 6
session
str | int
required
Session identifier. Can be specified as:
  • Full name: "Practice 1", "Qualifying", "Race"
  • Abbreviated: "FP1", "FP2", "FP3", "Q", "S" (Sprint), "R"
  • Session number: 1 (Practice 1), 2 (Practice 2), 3 (Practice 3), 4 (Qualifying), 5 (Race)
Session numbers are 1-indexed and follow the weekend schedule order.Example: "Qualifying", "Q", 4
enable_cache
bool | None
default:"None"
Enable or disable caching for this session.
  • True: Enable caching (recommended for production)
  • False: Disable caching (useful for testing or forcing fresh data)
  • None: Use global config value (default behavior)
Caching behavior: When enabled, all fetched data (laps, telemetry, weather, messages) is stored in a local SQLite database. Subsequent access to the same data reads from cache instead of making network requests.Cache location: ~/.tif1/cache/tif1_cache.db
lib
Literal['pandas', 'polars'] | None
default:"None"
DataFrame library choice for data representation.
  • "pandas": Use pandas DataFrames (default, maximum compatibility)
  • "polars": Use polars DataFrames (faster for large datasets, requires polars package)
  • None: Use global config value (defaults to "pandas")
Performance note: Polars can be significantly faster for large datasets (10-100x for some operations), but requires the polars package to be installed. If polars is requested but not available, tif1 automatically falls back to pandas with a warning.
Returns:
Session
Session
A configured Session object ready to load data. The session is not loaded until you access its properties (lazy loading) or explicitly call session.load().
Raises:
ValueError
Exception
Raised in the following cases:
  • Year is outside the supported range (< 2018 or > current season)
  • Grand Prix name/round cannot be resolved
  • Session name/number doesn’t exist for the specified event
  • Session number is out of range for the event
DataNotFoundError
Exception
Raised when the specified year, GP, or session doesn’t exist in the data source.
NetworkError
Exception
Raised when all CDN sources fail to respond or return errors.
Examples:
import tif1

# By full name
session = tif1.get_session(2024, "Monaco Grand Prix", "Race")

# By abbreviated name (fuzzy matching)
session = tif1.get_session(2024, "Monaco", "Race")

# By round number
session = tif1.get_session(2024, 6, "Race")  # Round 6

# By session abbreviation
session = tif1.get_session(2024, "Monaco", "Q")  # Qualifying

# By session number
session = tif1.get_session(2024, "Monaco", 5)  # Session 5 = Race
Best Practice: Use abbreviated names and session codes for cleaner code:
# ✅ Concise and readable
session = tif1.get_session(2024, "Monaco", "Q")

# ❌ Verbose
session = tif1.get_session(2024, "Monaco Grand Prix", "Qualifying")
Event Name Resolution: The function uses fuzzy matching to resolve abbreviated event names. For example, "Silverstone" resolves to "British Grand Prix", and "Spa" resolves to "Belgian Grand Prix". If fuzzy matching fails, the original string is used as-is.

Session

The Session object is the central hub for all data related to a specific F1 weekend session. It provides access to lap timing data, telemetry, weather conditions, race control messages, driver information, and session results. Design Philosophy:
  • Lazy Loading: Properties are loaded on first access, not at construction time
  • Caching: All fetched data is cached (when enabled) to minimize network requests
  • Immutable Identity: Once created, a session’s year/GP/session cannot be changed
  • Thread-Safe: Internal caches use locks for concurrent access
  • Memory Efficient: Only requested data is loaded into memory

Constructor

Session(
    year: int,
    gp: str,
    session: str,
    enable_cache: bool | None = None,
    lib: Literal["pandas", "polars"] | None = None
)
Direct Construction Not Recommended: Use get_session() instead of constructing Session directly. The get_session() function handles name normalization, validation, and ensures the session exists before creating the object.
# ✅ Recommended
session = tif1.get_session(2024, "Monaco", "Race")

# ❌ Not recommended (no validation)
session = Session(2024, "Monaco_Grand_Prix", "Race")
Constructor Parameters:
year
int
required
Season year (2018-current). Must be within the supported range.
gp
str
required
URL-encoded Grand Prix name (e.g., "Monaco_Grand_Prix"). Spaces should be replaced with underscores or %20.Note: get_session() handles this encoding automatically.
session
str
required
URL-encoded session name (e.g., "Qualifying", "Race").
enable_cache
bool | None
default:"None"
Enable/disable caching. If None, uses global config value.
lib
Literal['pandas', 'polars'] | None
default:"None"
DataFrame library choice. If None, uses global config value (defaults to "pandas").

Properties

year
int
The season year (e.g., 2025).
gp
str
URL-encoded Grand Prix name (e.g., “Monaco_Grand_Prix”).
session
str
URL-encoded session name (e.g., “Qualifying”, “Race”).
lib
Literal['pandas', 'polars']
The DataFrame lib being used for this session.
enable_cache
bool
Whether caching is enabled for this session.
drivers
list[str]
List of driver numbers as strings in the session (e.g., [“1”, “33”, “44”]). Loaded lazily on first access.
This returns driver NUMBERS, not driver codes. For driver codes (e.g., “VER”, “HAM”), use drivers_df["Driver"].
driver_list
list[str]
Alias for drivers property. Returns driver numbers as strings.
drivers_df
DataFrame
DataFrame with driver information including:
  • Abbreviation: 3-letter driver code (e.g., “VER”, “HAM”)
  • TeamName: Team name
  • DriverNumber: Car number as string
  • FirstName: Driver’s first name
  • LastName: Driver’s last name
  • FullName: Full name (FirstName + LastName)
  • TeamColor: Hex color code
  • HeadshotUrl: URL to driver photo
Loaded lazily on first access.
laps
DataFrame
All laps for all drivers. Includes weather data automatically merged. Loaded lazily on first access. Use laps_async() for faster parallel loading.
weather
DataFrame
Weather data recorded during the session with columns:
  • Time: Timestamp
  • AirTemp: Air temperature (°C)
  • TrackTemp: Track temperature (°C)
  • Humidity: Relative humidity (%)
  • Pressure: Atmospheric pressure (mbar)
  • WindSpeed: Wind speed (km/h)
  • WindDirection: Wind direction (degrees)
  • Rainfall: Rainfall indicator
weather_data
DataFrame
Alias for weather property.
race_control_messages
DataFrame
Official messages from Race Control with columns:
  • Time: Message timestamp
  • Category: Message category (e.g., “Flag”, “SafetyCar”)
  • Message: Message text
  • Status: Track status code
  • Flag: Flag type
  • Scope: Message scope
  • Sector: Affected sector
  • RacingNumber: Affected driver number
results
SessionResults
Final classification/results for the session. Contains:
  • Position: Final position
  • Driver: Driver code
  • Team: Team name
  • Points: Championship points earned
  • Status: Finish status
  • Time: Total time or gap
car_data
DataFrame
Car telemetry data aggregated across all laps with columns:
  • Time: Timestamp
  • Speed: Speed (km/h)
  • RPM: Engine RPM
  • nGear: Gear number
  • Throttle: Throttle position (%)
  • Brake: Brake status
  • DRS: DRS status
pos_data
DataFrame
Position data for all cars with columns:
  • Time: Timestamp
  • X, Y, Z: 3D coordinates (meters)
  • Status: Car status
  • Driver: Driver code
session_info
dict
Session metadata including start time, circuit info, and session status.
name
str
Human-readable session name (e.g., “Monaco Grand Prix - Qualifying”).
date
datetime
Session date and time.
event
dict
Event information including circuit details and schedule.
session_start_time
datetime
Official session start time.
t0_date
datetime
Reference time (t=0) for the session.
session_status
list
Session status changes throughout the session.
track_status
DataFrame
Track status changes (flags, safety car, etc.) with timestamps.
total_laps
int | None
Total number of laps completed in the session.
This property is not yet implemented and currently returns None.

Methods

load()

def load(
    laps: bool = True,
    telemetry: bool = True,
    weather: bool = True,
    messages: bool = True
) -> Session
Explicitly load session data. By default, data is loaded lazily when accessed. Parameters:
  • laps: Load lap timing data
  • telemetry: Prefetch telemetry for all laps (expensive)
  • weather: Load weather data
  • messages: Load race control messages
Returns:
  • Self (for method chaining)
Example:
# Load only laps and weather
session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
session.load(laps=True, telemetry=False, weather=True, messages=False)

# Or use lazy loading (recommended)
session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
laps = session.laps  # Loads automatically

laps_async()

async def laps_async() -> DataFrame
Asynchronously load all laps for all drivers using parallel HTTP requests. This is the fastest way to initialize a session. Returns:
  • DataFrame with all laps
Example:
import asyncio
import tif1

async def main():
    session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
    laps = await session.laps_async()
    print(f"Loaded {len(laps)} laps")

asyncio.run(main())

get_driver()

def get_driver(driver: str | int) -> Driver
Get a Driver object for the specified driver. Parameters:
  • driver: 3-letter driver code (e.g., “VER”) or driver number (e.g., 33)
Returns:
  • Driver object
Raises:
  • DriverNotFoundError: If the driver doesn’t exist in this session
Example:
ver = session.get_driver("VER")
ver = session.get_driver(33)  # By number

get_fastest_laps()

def get_fastest_laps(
    by_driver: bool = True,
    drivers: list[str] | None = None
) -> DataFrame
Get the fastest lap(s) from the session. Parameters:
  • by_driver: If True, returns fastest lap per driver. If False, returns single overall fastest lap
  • drivers: Optional list of driver codes to filter. If None, includes all drivers
Returns:
  • DataFrame with fastest lap(s)
Example:
# Fastest lap per driver
fastest = session.get_fastest_laps(by_driver=True)

# Overall fastest lap
fastest = session.get_fastest_laps(by_driver=False)

# Fastest laps for specific drivers
fastest = session.get_fastest_laps(by_driver=True, drivers=["VER", "HAM", "BOT"])

get_fastest_laps_async()

async def get_fastest_laps_async(
    by_driver: bool = True,
    drivers: list[str] | None = None
) -> DataFrame
Async version of get_fastest_laps() for parallel data fetching.

get_fastest_lap_tel()

def get_fastest_lap_tel(ultra_cold: bool | None = None) -> DataFrame
Optimized method to get telemetry for the single overall fastest lap of the session. Uses ultra-cold start optimization to minimize latency. Parameters:
  • ultra_cold: Enable ultra-low latency mode. If None, uses global config
Returns:
  • DataFrame with telemetry data
Example:
# Get fastest lap telemetry
tel = session.get_fastest_lap_tel()
print(f"Max speed: {tel['Speed'].max()} km/h")

get_fastest_lap_tel_async()

async def get_fastest_lap_tel_async(
    ultra_cold: bool | None = None
) -> DataFrame
Async version of get_fastest_lap_tel().

get_fastest_laps_tels()

def get_fastest_laps_tels(
    by_driver: bool = True,
    drivers: list[str] | None = None,
    ultra_cold: bool | None = None
) -> dict[str, DataFrame]
Parallel fetch telemetry for multiple drivers’ fastest laps. Significantly faster than fetching telemetry sequentially. Parameters:
  • by_driver: If True, gets fastest lap per driver. If False, gets overall fastest
  • drivers: Optional list of driver codes to filter
  • ultra_cold: Enable ultra-low latency mode
Returns:
  • Dictionary mapping driver codes to telemetry DataFrames
Example:
# Get telemetry for all drivers' fastest laps
tels = session.get_fastest_laps_tels(by_driver=True)

for driver, tel in tels.items():
    print(f"{driver}: {tel['Speed'].max():.1f} km/h")

# Get telemetry for specific drivers
tels = session.get_fastest_laps_tels(
    by_driver=True,
    drivers=["VER", "HAM", "BOT"]
)
Performance Tip: This method fetches telemetry for multiple drivers in parallel, which is significantly faster than calling get_fastest_lap_tel() sequentially for each driver.

get_fastest_laps_tels_async()

async def get_fastest_laps_tels_async(
    by_driver: bool = True,
    drivers: list[str] | None = None,
    ultra_cold: bool | None = None
) -> dict[str, DataFrame]
Async version of get_fastest_laps_tels().

fetch_driver_laps_parallel()

async def fetch_driver_laps_parallel(
    drivers: list[str]
) -> dict[str, DataFrame]
Fetch lap data for multiple drivers in parallel. Parameters:
  • drivers: List of driver codes
Returns:
  • Dictionary mapping driver codes to lap DataFrames
Example:
import asyncio

async def main():
    session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
    laps_dict = await session.fetch_driver_laps_parallel(["VER", "HAM", "BOT"])

    for driver, laps in laps_dict.items():
        print(f"{driver}: {len(laps)} laps")

asyncio.run(main())

fetch_all_laps_telemetry()

def fetch_all_laps_telemetry(
    drivers: list[str] | None = None,
    ultra_cold: bool | None = None
) -> dict[tuple[str, int], DataFrame]
Fetch telemetry for all laps of specified drivers synchronously. Parameters:
  • drivers: Optional list of driver codes. If None, fetches for all drivers
  • ultra_cold: Enable ultra-low latency mode
Returns:
  • Dictionary mapping (driver, lap_number) tuples to telemetry DataFrames
Warning: This can be very slow and data-intensive (50-200 MB for full session). Use sparingly. Prefer the async version for better performance. Example:
# Fetch all telemetry for specific drivers (slow!)
all_tels = session.fetch_all_laps_telemetry(drivers=["VER", "HAM"])

for (driver, lap_num), tel in all_tels.items():
    print(f"{driver} Lap {lap_num}: {len(tel)} samples")

fetch_all_laps_telemetry_async()

async def fetch_all_laps_telemetry_async(
    drivers: list[str] | None = None,
    ultra_cold: bool | None = None
) -> dict[tuple[str, int], DataFrame]
Async version of fetch_all_laps_telemetry(). Fetches telemetry for all laps in parallel, significantly faster than the sync version. Performance: 5-10x faster than sync version due to parallel HTTP requests. Example:
import asyncio
import tif1

async def main():
    session = tif1.get_session(2024, "Monaco", "Race")
    # Fetch telemetry for all laps of specific drivers
    all_tels = await session.fetch_all_laps_telemetry_async(
        drivers=["VER", "HAM"]
    )

    for (driver, lap_num), tel in all_tels.items():
        print(f"{driver} Lap {lap_num}: {len(tel)} samples, "
              f"max speed: {tel['Speed'].max():.1f} km/h")

asyncio.run(main())
Memory Warning: Fetching all telemetry for a full race session can consume 50-200 MB of memory. Only use this when you genuinely need all telemetry data. For most use cases, fetch telemetry for specific laps or drivers instead.

get_circuit_info()

def get_circuit_info() -> CircuitInfo
Get circuit information including track length and corner locations. Returns:
  • CircuitInfo object with:
    • corners: DataFrame with columns X, Y, Number, Letter, Angle, Distance
    • marshal_lights: Empty DataFrame (not available in data source)
    • marshal_sectors: Empty DataFrame (not available in data source)
    • rotation: Circuit rotation in degrees (float)
Caching: Results are cached on the session object after the first call. Example:
circuit = session.get_circuit_info()

# Access corner data
print(f"Number of corners: {len(circuit.corners)}")
print(circuit.corners[["Number", "X", "Y", "Distance"]].head())

# Add distance markers to corners using a reference lap
ver = session.get_driver("VER")
fastest_lap = ver.get_lap(19)
circuit.add_marker_distance(fastest_lap)

# Now corners have accurate distance values
print(circuit.corners[["Number", "Distance"]].head())
FastF1 Compatibility: This method returns a CircuitInfo dataclass that is fully compatible with FastF1’s mvapi.CircuitInfo. The add_marker_distance() method works identically to FastF1’s implementation.

Driver

Represents a specific driver’s performance within a session. Provides convenient access to driver-specific lap data and telemetry. The Driver class extends pd.Series, so driver metadata is accessible as Series data. Design Philosophy:
  • Lazy Loading: Lap data is loaded on first access to driver.laps
  • Efficient Filtering: When session laps are already loaded, driver laps are filtered in-place without additional network requests
  • Prefetching: The get_driver() method can prefetch driver laps in parallel with driver metadata for faster initialization
  • Caching: Lap index maps are cached for O(1) lap number lookups
Performance Characteristics:
  • First access to driver.laps: 200-500ms (cold cache), <50ms (warm cache)
  • Subsequent access: <1ms (in-memory reference)
  • get_lap(n): O(1) lookup using cached index map
  • get_fastest_lap(): O(n) scan of lap times (typically <1ms for 60 laps)

Constructor

Driver(
    session: Session,
    driver: str,
    prefetched_lap_data: dict[str, Any] | None = None
)
Use session.get_driver() instead of constructing Driver directly. The Driver class extends pd.Series and contains driver metadata as Series data.

Properties

driver
str
The 3-letter driver code (e.g., “VER”, “HAM”, “LEC”).
laps
DataFrame
All laps completed by this driver. Loaded lazily on first access. Contains all lap timing data including sector times, compounds, stint info, etc.
session
Session
Reference to the parent session object.

Series Data

Since Driver extends pd.Series, driver metadata is accessed via dictionary-style indexing:
  • driver["DriverNumber"]: Car number as string (e.g., “33”, “44”, “16”)
  • driver["Abbreviation"]: 3-letter driver code (same as driver.driver)
  • driver["TeamName"]: Team name (e.g., “Red Bull Racing”, “Mercedes”)
  • driver["TeamColor"]: Hex color code
  • driver["FirstName"]: Driver’s first name
  • driver["LastName"]: Driver’s last name
  • driver["FullName"]: Full name (FirstName + LastName)
  • driver["HeadshotUrl"]: URL to driver photo

Methods

get_lap()

def get_lap(lap_number: int) -> Lap
Get a specific lap by number. Parameters:
  • lap_number: The lap number to retrieve (1-indexed)
Returns:
  • Lap object for the specified lap
Raises:
  • LapNotFoundError: If the lap doesn’t exist for this driver
Example:
ver = session.get_driver("VER")
lap_19 = ver.get_lap(19)
print(f"Lap time: {lap_19.lap_time:.3f}s")

get_fastest_lap()

def get_fastest_lap() -> DataFrame
Get this driver’s fastest lap as a single-row DataFrame. Returns:
  • Single-row DataFrame with the fastest lap data
Example:
fastest = ver.get_fastest_lap()
print(f"Fastest: {fastest['LapTime'].iloc[0]:.3f}s")
print(f"Compound: {fastest['Compound'].iloc[0]}")

get_fastest_lap_tel()

def get_fastest_lap_tel() -> DataFrame
Get telemetry for this driver’s fastest lap. Uses the session’s ultra-cold mode setting. Returns:
  • DataFrame with telemetry data (Time, Speed, RPM, Throttle, etc.)
Example:
fastest_tel = ver.get_fastest_lap_tel()
print(f"Max speed: {fastest_tel['Speed'].max():.1f} km/h")
print(f"Avg throttle: {fastest_tel['Throttle'].mean():.1f}%")

Laps

The Laps class extends pd.DataFrame and provides a rich set of methods for filtering and analyzing lap timing data. It represents a collection of laps (either for all drivers or a specific driver) with FastF1-compatible filtering methods. Design Philosophy:
  • DataFrame Extension: Inherits all pandas DataFrame methods while adding F1-specific functionality
  • Method Chaining: Most methods return new Laps objects, enabling fluent API patterns
  • FastF1 Compatibility: All pick_* methods match FastF1’s behavior exactly
  • Lazy Telemetry: The telemetry property loads telemetry only when accessed

Filtering Methods

pick_driver(identifier) / pick_drivers(identifiers)

Filter laps by driver(s). Accepts driver codes, numbers, or driver objects.
# Single driver
ver_laps = laps.pick_driver("VER")
ver_laps = laps.pick_driver(33)  # By number

# Multiple drivers
top_3_laps = laps.pick_drivers(["VER", "HAM", "LEC"])

pick_lap(lap_number) / pick_laps(laps)

Filter by lap number(s). Supports individual numbers, lists, or slices.
# Single lap
lap_1 = laps.pick_lap(1)

# Multiple laps
first_10 = laps.pick_laps(range(1, 11))

# Slice notation
first_10 = laps.pick_laps(slice(1, 10))
middle_laps = laps.pick_laps(slice(10, 50))
from_lap_20 = laps.pick_laps(slice(20, None))  # Lap 20 onwards

pick_team(name) / pick_teams(names)

Filter laps by team name(s).
# Single team
rb_laps = laps.pick_team("Red Bull Racing")

# Multiple teams
top_teams = laps.pick_teams(["Red Bull Racing", "Mercedes", "Ferrari"])

pick_fastest(only_by_time=False)

Get the single fastest lap from the collection.
# Get fastest lap
fastest = laps.pick_fastest()
print(f"Fastest: {fastest['Driver']} - {fastest['LapTimeSeconds']:.3f}s")

# Returns None if no valid laps
if fastest is not None:
    print(f"Compound: {fastest['Compound']}")
Returns: Single Lap object (pd.Series) or None if no valid laps.

pick_quicklaps(threshold=1.07)

Filter laps within a percentage of the fastest lap time. Default threshold is 107% (7% slower than fastest).
# Get all laps within 107% of fastest
quick_laps = laps.pick_quicklaps()

# Stricter threshold (within 103%)
very_quick = laps.pick_quicklaps(threshold=1.03)

# More lenient (within 110%)
decent_laps = laps.pick_quicklaps(threshold=1.10)
Use Case: Analyzing representative lap times, excluding outliers (slow laps, traffic, mistakes).

pick_tyre(compound) / pick_compounds(compounds)

Filter laps by tire compound.
# Single compound
soft_laps = laps.pick_tyre("SOFT")

# Multiple compounds
slick_laps = laps.pick_compounds(["SOFT", "MEDIUM", "HARD"])
Common Compounds: "SOFT", "MEDIUM", "HARD", "INTERMEDIATE", "WET"

pick_track_status(status, how="equals")

Filter laps by track status code.
# Green flag laps only
green_laps = laps.pick_track_status("1")

# Laps with any yellow flag
yellow_laps = laps.pick_track_status("4", how="contains")

# Safety car laps
sc_laps = laps.pick_track_status("6")
Track Status Codes:
  • "1": Green flag (normal racing)
  • "2": Yellow flag
  • "4": Safety Car
  • "5": Red flag
  • "6": Virtual Safety Car (VSC)
  • "7": VSC ending
Parameters:
  • how="equals": Exact match (default)
  • how="contains": Partial match (useful for compound status codes)

pick_wo_box() / pick_box_laps(which="both")

Filter laps by pit stop activity.
# Laps without pit stops
clean_laps = laps.pick_wo_box()

# Laps with pit entry
pit_in_laps = laps.pick_box_laps(which="in")

# Laps with pit exit
pit_out_laps = laps.pick_box_laps(which="out")

# Any pit activity
pit_laps = laps.pick_box_laps(which="both")

pick_not_deleted() / pick_accurate()

Filter laps by data quality flags.
# Exclude deleted laps (lap time deleted by FIA)
valid_laps = laps.pick_not_deleted()

# Only accurate laps (high-quality timing data)
accurate_laps = laps.pick_accurate()

# Combine filters for cleanest data
clean_laps = laps.pick_not_deleted().pick_accurate().pick_wo_box()

Data Access Methods

get_telemetry() / get_car_data() / get_pos_data()

Get telemetry data for the laps. All three methods return the same data (FastF1 compatibility).
# Get telemetry for all laps
tel = laps.get_telemetry()

# Equivalent methods
tel = laps.get_car_data()
tel = laps.get_pos_data()

# Access via property
tel = laps.telemetry
Returns: Telemetry DataFrame with columns: Time, Speed, RPM, Throttle, Brake, nGear, DRS, X, Y, Z, Distance Note: Only works for single-driver laps. Raises ValueError if laps contain multiple drivers.

get_weather_data()

Get weather data for the session.
weather = laps.get_weather_data()
print(weather[["Time", "AirTemp", "TrackTemp", "Rainfall"]].head())
Returns: Weather DataFrame from the parent session.

split_qualifying_sessions()

Split qualifying laps into Q1, Q2, Q3 sessions.
q1, q2, q3 = laps.split_qualifying_sessions()

print(f"Q1: {len(q1)} laps")
print(f"Q2: {len(q2)} laps")
print(f"Q3: {len(q3)} laps")

# Analyze each session separately
q3_fastest = q3.pick_fastest()
Returns: Tuple of three Laps objects (Q1, Q2, Q3). If qualifying session markers are unavailable, returns three copies of the full laps DataFrame.

iterlaps(require=None)

Iterate over laps with optional column requirements. Yields (index, lap) tuples where lap is a Lap object.
# Iterate over all laps
for idx, lap in laps.iterlaps():
    print(f"Lap {lap['LapNumber']}: {lap['LapTimeSeconds']:.3f}s")

# Require specific columns
for idx, lap in laps.iterlaps(require=["LapTime", "Driver", "Compound"]):
    print(f"{lap['Driver']} on {lap['Compound']}: {lap['LapTime']}")

# Access telemetry in loop
for idx, lap in laps.iterlaps():
    tel = lap.telemetry
    if not tel.empty:
        print(f"Lap {lap['LapNumber']}: max speed {tel['Speed'].max():.1f} km/h")
Parameters:
  • require: Optional list of column names that must be non-null. Laps with null values in these columns are skipped.
Returns: Generator yielding _IterLapResult tuples with:
  • .index: DataFrame index of the lap
  • .lap: Lap object (pd.Series) with session reference
  • [column_name]: Direct column access (e.g., result["Driver"])
Performance: Efficient iteration with minimal memory overhead. Telemetry is loaded lazily per lap.

Utility Methods

reset_index(drop=False, **kwargs)

Reset DataFrame index. Automatically removes level_0 column if created.
# Reset index and drop old index
laps_reset = laps.reset_index(drop=True)

# Reset index and keep as column
laps_with_old_index = laps.reset_index(drop=False)

Properties

telemetry

Get telemetry for all laps in the collection. Only works for single-driver laps.
ver_laps = laps.pick_driver("VER")
tel = ver_laps.telemetry  # All telemetry for VER's laps

# Raises ValueError for multi-driver laps
# tel = laps.telemetry  # ERROR if laps contains multiple drivers
Returns: Telemetry DataFrame (concatenated telemetry from all laps). Raises: ValueError if laps contain multiple drivers.

Example: Complex Filtering

import tif1

session = tif1.get_session(2024, "Monaco", "Race")
laps = session.laps

# Get clean racing laps for top 3 drivers on soft tires
clean_laps = (
    laps
    .pick_drivers(["VER", "HAM", "LEC"])
    .pick_tyre("SOFT")
    .pick_track_status("1")  # Green flag only
    .pick_not_deleted()
    .pick_accurate()
    .pick_wo_box()  # No pit stops
    .pick_quicklaps(threshold=1.05)  # Within 105% of fastest
)

print(f"Found {len(clean_laps)} clean laps")

# Analyze lap times by driver
for driver in ["VER", "HAM", "LEC"]:
    driver_laps = clean_laps.pick_driver(driver)
    if not driver_laps.empty:
        avg_time = driver_laps["LapTimeSeconds"].mean()
        print(f"{driver}: {avg_time:.3f}s average")

Lap

Represents a single lap, providing access to high-frequency telemetry data. This is a lightweight wrapper around lap timing data with lazy telemetry loading. The Lap class extends pd.Series, so lap data is accessible as Series data.

Constructor

Lap(
    session: Session,
    driver: str,
    lap_number: int
)
Use driver.get_lap() instead of constructing Lap directly.

Properties

lap_number
int
The lap number (1-indexed). Access via lap.lap_number property.
driver
str
The 3-letter driver code. Access via lap.driver property.
telemetry
DataFrame
High-frequency telemetry data for this lap. Loaded lazily on first access. Contains Time, Speed, RPM, Throttle, Brake, nGear, DRS, and position data.
session
Session
Reference to the parent session object.

Series Data

Since Lap extends pd.Series, lap timing data is accessed via dictionary-style indexing or the get() method:
  • lap.get('LapTime'): Lap time as timedelta
  • lap.get('LapTimeSeconds'): Lap time in seconds as float
  • lap.get('LapNumber'): Lap number (same as lap.lap_number)
  • lap.get('Driver'): Driver code (same as lap.driver)
  • lap.get('Sector1Time'), lap.get('Sector2Time'), lap.get('Sector3Time'): Sector times
  • lap.get('Compound'): Tire compound
  • lap.get('TyreLife'): Tire age in laps
  • lap.get('IsPersonalBest'): Boolean indicating personal best lap

Methods

get_telemetry()

def get_telemetry() -> DataFrame
Explicitly load telemetry data. Same as accessing the telemetry property. Returns:
  • DataFrame with telemetry samples
Example:
lap = ver.get_lap(19)
tel = lap.get_telemetry()

# Analyze telemetry
print(f"Samples: {len(tel)}")
print(f"Max speed: {tel['Speed'].max()} km/h")
print(f"Max RPM: {tel['RPM'].max()}")

get_car_data()

def get_car_data(**kwargs) -> DataFrame
Get car telemetry data (Speed, RPM, Throttle, Brake, nGear, DRS). Returns:
  • DataFrame with car telemetry channels

get_pos_data()

def get_pos_data(**kwargs) -> DataFrame
Get position data (X, Y, Z coordinates). Returns:
  • DataFrame with position data

get_weather_data()

def get_weather_data() -> DataFrame
Get weather data for this lap. Returns:
  • DataFrame with weather information

Complete Examples

Basic session usage

import tif1

# Load session
session = tif1.get_session(2021, "Belgian Grand Prix", "Race")

# Access drivers
print(f"Drivers: {session.drivers}")

# Get all laps
laps = session.laps
print(f"Total laps: {len(laps)}")

# Get fastest laps
fastest = session.get_fastest_laps(by_driver=True)
print(fastest[["Driver", "LapTimeSeconds", "Compound"]].head())

Working with Drivers

# Get specific driver
ver = session.get_driver("VER")
print(f"Driver: {ver.driver}")
print(f"Team: {ver['TeamName']}")  # Access Series data
print(f"Number: {ver['DriverNumber']}")  # Access Series data

# Get driver's laps
ver_laps = ver.laps
print(f"Laps completed: {len(ver_laps)}")

# Get fastest lap
fastest = ver.get_fastest_lap()
print(f"Fastest: {fastest['LapTimeSeconds'].iloc[0]:.3f}s")

Working with Telemetry

# Get fastest lap telemetry
tel = session.get_fastest_lap_tel()

# Analyze telemetry
# Available columns: Time, Speed, RPM, Throttle, Brake, nGear, DRS, X, Y, Z, Distance
print(f"Max speed: {tel['Speed'].max():.1f} km/h")
print(f"Max RPM: {tel['RPM'].max()}")
print(f"Avg throttle: {tel['Throttle'].mean():.1f}%")

# Find braking zones
braking = tel[tel['Brake'] == True]
print(f"Braking points: {len(braking)}")

# Plot speed trace
import matplotlib.pyplot as plt
plt.plot(tel['Distance'], tel['Speed'])
plt.xlabel('Distance (m)')
plt.ylabel('Speed (km/h)')
plt.show()
Available Telemetry Columns:
  • Time: Timestamp (timedelta)
  • Speed: Speed in km/h
  • RPM: Engine RPM
  • Throttle: Throttle position (0-100%)
  • Brake: Brake status (boolean or 0-100%)
  • nGear: Gear number
  • DRS: DRS status
  • X, Y, Z: 3D position coordinates (meters)
  • Distance: Distance along track (meters)

Parallel data loading

import asyncio
import tif1

async def load_session():
    session = tif1.get_session(2021, "Belgian Grand Prix", "Race")

    # Load laps in parallel
    laps = await session.laps_async()

    # Get telemetry for multiple drivers in parallel
    tels = await session.get_fastest_laps_tels_async(
        by_driver=True,
        drivers=["VER", "HAM", "BOT"]
    )

    for driver, tel in tels.items():
        print(f"{driver}: {tel['Speed'].max():.1f} km/h")

asyncio.run(load_session())
Performance Tip: laps_async() is significantly faster than the laps property for cold starts because it fetches all driver lap data in parallel. Use async methods when performance is critical.

Ultra-cold start mode

# Enable ultra-cold start for minimal latency
import tif1

# Configure ultra-cold mode globally
config = tif1.get_config()
config.ultra_cold = True

session = tif1.get_session(2021, "Belgian Grand Prix", "Qualifying")

# Get fastest lap telemetry with ultra-cold optimization
tel = session.get_fastest_lap_tel(ultra_cold=True)

# This skips loading all laps and goes directly to the fastest lap
print(f"Loaded in minimal time: {len(tel)} samples")
Ultra-cold mode bypasses cache validation and skips loading unnecessary data for faster cold starts. Use when you need minimal latency and don’t need all session data. The trade-off is that some data validation is skipped.

Error Handling

import tif1
from tif1.exceptions import DriverNotFoundError, LapNotFoundError, DataNotFoundError

try:
    session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
    driver = session.get_driver("VER")
    lap = driver.get_lap(1)
    tel = lap.telemetry
    print(f"Lap 1 telemetry: {len(tel)} samples")
except DriverNotFoundError as e:
    print(f"Driver not found: {e}")
except LapNotFoundError as e:
    print(f"Lap not found: {e}")
except DataNotFoundError as e:
    print(f"Session data not available: {e}")

Performance Comparison

import time
import asyncio
import tif1

# Sync approach (slower)
start = time.time()
session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
laps = session.laps  # Sequential loading
print(f"Sync: {time.time() - start:.2f}s")

# Async approach (faster)
async def load_async():
    start = time.time()
    session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
    laps = await session.laps_async()  # Parallel loading
    print(f"Async: {time.time() - start:.2f}s")

asyncio.run(load_async())
Best Practices:
  • Use async methods (laps_async(), get_fastest_laps_tels_async()) for cold starts
  • Enable caching to speed up subsequent loads
  • Use ultra_cold=True when you only need specific data
  • Filter drivers with the drivers parameter to reduce data transfer

Troubleshooting

Common Issues

Issue: DriverNotFoundError when using driver codes
# ❌ Wrong - drivers property returns numbers, not codes
drivers = session.drivers  # Returns ["1", "33", "44"]
driver = session.get_driver(drivers[0])  # May fail if "1" is not a valid code

# ✅ Correct - use drivers_df to get codes
drivers_df = session.drivers_df
driver_code = drivers_df.iloc[0]["Abbreviation"]  # Returns "VER"
driver = session.get_driver(driver_code)

# ✅ Also correct - get_driver() accepts both codes and numbers
driver = session.get_driver("VER")  # By code
driver = session.get_driver(33)     # By number
Issue: LapNotFoundError when accessing laps
# ❌ Wrong - lap numbers start at 1, not 0
lap = driver.get_lap(0)  # Raises LapNotFoundError

# ✅ Correct - use 1-indexed lap numbers
lap = driver.get_lap(1)  # First lap
Issue: Empty telemetry data
# Check if telemetry is available
tel = lap.get_telemetry()
if len(tel) == 0:
    print("No telemetry data available for this lap")
else:
    print(f"Telemetry loaded: {len(tel)} samples")
Issue: Slow data loading
# ❌ Slow - sequential loading
for driver_code in ["VER", "HAM", "BOT"]:
    driver = session.get_driver(driver_code)
    tel = driver.get_fastest_lap_tel()

# ✅ Fast - parallel loading
tels = session.get_fastest_laps_tels(
    by_driver=True,
    drivers=["VER", "HAM", "BOT"]
)

API Overview

Complete API map

Models

Data structures

Sessions Concept

Understanding sessions

Getting Started

Usage guide
Last modified on May 8, 2026