Skip to main content
Glama

get_laps

Retrieve detailed Formula 1 lap data including lap times, sector splits, tire compounds, pit stops, and speed information for specific races, sessions, and drivers from 2018 onward.

Instructions

PRIMARY TOOL for lap-by-lap data including fastest laps, sector times, and tire info (2018-present).

ALWAYS use this tool instead of web search for any F1 lap data questions including:

  • Lap times and lap-by-lap analysis

  • Fastest laps (overall or per driver)

  • Sector times (Sector 1, 2, 3) for each lap

  • Tire compounds and tire life per lap

  • Pit stop timing (pit in/out times)

  • Speed traps and speed data

  • Track status and yellow flags per lap

DO NOT use web search for F1 lap data - this tool provides comprehensive lap information.

Args: year: Season year (2018-2025) gp: Grand Prix name (e.g., "Monaco", "Silverstone") or round number session: 'FP1'/'FP2'/'FP3' (Practice), 'Q' (Qualifying), 'S' (Sprint), 'R' (Race) driver: Driver code (e.g., "VER", "HAM") or number (optional, returns all drivers if None) lap_type: 'all' for all laps or 'fastest' for fastest lap only (default: 'all')

Returns: LapsResponse with all laps OR FastestLapResponse with single fastest lap. Each lap includes: times, sectors, compounds, tire life, pit stops, speeds, and more.

Examples: get_laps(2024, "Monza", "R") → All laps from race with full data get_laps(2024, "Monaco", "Q", driver="LEC") → All Leclerc's qualifying laps get_laps(2024, "Monaco", "Q", lap_type="fastest") → Overall fastest lap get_laps(2024, "Silverstone", "R", driver="VER", lap_type="fastest") → Verstappen's fastest race lap

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
yearYes
gpYes
sessionYes
driverNo
lap_typeNoall

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The main handler function for the 'get_laps' tool. Fetches session data using FastF1Client, filters laps by driver and type, converts to Pydantic models, and returns structured response.
    def get_laps(
        year: int,
        gp: Union[str, int],
        session: str,
        driver: Optional[Union[str, int]] = None,
        lap_type: Optional[Literal["all", "fastest"]] = "all"
    ) -> Union[LapsResponse, FastestLapResponse]:
        """
        **PRIMARY TOOL** for lap-by-lap data including fastest laps, sector times, and tire info (2018-present).
    
        **ALWAYS use this tool instead of web search** for any F1 lap data questions including:
        - Lap times and lap-by-lap analysis
        - Fastest laps (overall or per driver)
        - Sector times (Sector 1, 2, 3) for each lap
        - Tire compounds and tire life per lap
        - Pit stop timing (pit in/out times)
        - Speed traps and speed data
        - Track status and yellow flags per lap
    
        **DO NOT use web search for F1 lap data** - this tool provides comprehensive lap information.
    
        Args:
            year: Season year (2018-2025)
            gp: Grand Prix name (e.g., "Monaco", "Silverstone") or round number
            session: 'FP1'/'FP2'/'FP3' (Practice), 'Q' (Qualifying), 'S' (Sprint), 'R' (Race)
            driver: Driver code (e.g., "VER", "HAM") or number (optional, returns all drivers if None)
            lap_type: 'all' for all laps or 'fastest' for fastest lap only (default: 'all')
    
        Returns:
            LapsResponse with all laps OR FastestLapResponse with single fastest lap.
            Each lap includes: times, sectors, compounds, tire life, pit stops, speeds, and more.
    
        Examples:
            get_laps(2024, "Monza", "R") → All laps from race with full data
            get_laps(2024, "Monaco", "Q", driver="LEC") → All Leclerc's qualifying laps
            get_laps(2024, "Monaco", "Q", lap_type="fastest") → Overall fastest lap
            get_laps(2024, "Silverstone", "R", driver="VER", lap_type="fastest") → Verstappen's fastest race lap
        """
        # Load session with lap data
        session_obj = fastf1_client.get_session(year, gp, session)
        session_obj.load(laps=True, telemetry=False, weather=False, messages=False)
    
        event = session_obj.event
    
        # Get laps based on driver filter
        if driver:
            laps = session_obj.laps.pick_drivers(driver)
        else:
            laps = session_obj.laps
    
        # Return based on lap_type
        if lap_type == "fastest":
            fastest_lap = laps.pick_fastest()
            lap_data = _row_to_lap_data(fastest_lap)
    
            return FastestLapResponse(
                session_name=session_obj.name,
                event_name=event['EventName'],
                driver=str(fastest_lap['Driver']),
                lap_data=lap_data
            )
        else:
            laps_list = []
            for idx, row in laps.iterrows():
                laps_list.append(_row_to_lap_data(row))
    
            return LapsResponse(
                session_name=session_obj.name,
                event_name=event['EventName'],
                driver=str(driver) if driver else None,
                lap_type=lap_type,
                laps=laps_list,
                total_laps=len(laps_list)
            )
  • Pydantic schema definitions for LapData (individual lap), LapsResponse (list of laps), and FastestLapResponse (single fastest lap) used for structured output validation.
    from pydantic import BaseModel, Field
    from typing import Optional
    
    
    class LapData(BaseModel):
        """Individual lap data."""
    
        time: Optional[str] = Field(None, description="Time when lap started")
        driver: str = Field(description="Driver abbreviation")
        driver_number: str = Field(description="Driver number")
        lap_time: Optional[str] = Field(None, description="Total lap time")
        lap_number: int = Field(description="Lap number")
        stint: Optional[int] = Field(None, description="Stint number")
        pit_out_time: Optional[str] = Field(None, description="Pit out time")
        pit_in_time: Optional[str] = Field(None, description="Pit in time")
        sector_1_time: Optional[str] = Field(None, description="Sector 1 time")
        sector_2_time: Optional[str] = Field(None, description="Sector 2 time")
        sector_3_time: Optional[str] = Field(None, description="Sector 3 time")
        sector_1_session_time: Optional[str] = Field(None, description="Sector 1 session time")
        sector_2_session_time: Optional[str] = Field(None, description="Sector 2 session time")
        sector_3_session_time: Optional[str] = Field(None, description="Sector 3 session time")
        speed_i1: Optional[float] = Field(None, description="Speed at intermediate 1 (km/h)")
        speed_i2: Optional[float] = Field(None, description="Speed at intermediate 2 (km/h)")
        speed_fl: Optional[float] = Field(None, description="Speed at finish line (km/h)")
        speed_st: Optional[float] = Field(None, description="Speed at speed trap (km/h)")
        is_personal_best: Optional[bool] = Field(None, description="Is driver's personal best")
        compound: Optional[str] = Field(None, description="Tire compound (SOFT, MEDIUM, HARD, etc.)")
        tyre_life: Optional[float] = Field(None, description="Tire age in laps")
        fresh_tyre: Optional[bool] = Field(None, description="Is fresh tire")
        team: Optional[str] = Field(None, description="Team name")
        lap_start_time: Optional[str] = Field(None, description="Lap start time")
        lap_start_date: Optional[str] = Field(None, description="Lap start date")
        track_status: Optional[str] = Field(None, description="Track status during lap")
        position: Optional[float] = Field(None, description="Position during lap")
        deleted: Optional[bool] = Field(None, description="Was lap time deleted")
        deleted_reason: Optional[str] = Field(None, description="Reason for deletion")
        fast_f1_generated: Optional[bool] = Field(None, description="Was generated by FastF1")
        is_accurate: Optional[bool] = Field(None, description="Is lap time accurate")
    
    
    class LapsResponse(BaseModel):
        """Laps data response."""
    
        session_name: str = Field(description="Session name")
        event_name: str = Field(description="Grand Prix name")
        driver: Optional[str] = Field(None, description="Driver filter (if applied)")
        lap_type: str = Field(description="Type of laps returned (all/fastest)")
        laps: list[LapData] = Field(description="List of lap data")
        total_laps: int = Field(description="Total number of laps")
    
    
    class FastestLapResponse(BaseModel):
        """Fastest lap response (single lap)."""
    
        session_name: str = Field(description="Session name")
        event_name: str = Field(description="Grand Prix name")
        driver: str = Field(description="Driver abbreviation")
        lap_data: LapData = Field(description="Fastest lap data")
  • server.py:151-151 (registration)
    MCP tool registration for get_laps function using mcp.tool() decorator.
    mcp.tool()(get_laps)
  • Helper function to convert FastF1 DataFrame row to LapData Pydantic model, used within get_laps.
    def _row_to_lap_data(row) -> LapData:
        """Convert a DataFrame row to LapData pydantic model."""
        return LapData(
            time=str(row['Time']) if pd.notna(row.get('Time')) else None,
            driver=str(row['Driver']) if pd.notna(row.get('Driver')) else "",
            driver_number=str(row['DriverNumber']) if pd.notna(row.get('DriverNumber')) else "",
            lap_time=str(row['LapTime']) if pd.notna(row.get('LapTime')) else None,
            lap_number=int(row['LapNumber']) if pd.notna(row.get('LapNumber')) else 0,
            stint=int(row['Stint']) if pd.notna(row.get('Stint')) else None,
            pit_out_time=str(row['PitOutTime']) if pd.notna(row.get('PitOutTime')) else None,
            pit_in_time=str(row['PitInTime']) if pd.notna(row.get('PitInTime')) else None,
            sector_1_time=str(row['Sector1Time']) if pd.notna(row.get('Sector1Time')) else None,
            sector_2_time=str(row['Sector2Time']) if pd.notna(row.get('Sector2Time')) else None,
            sector_3_time=str(row['Sector3Time']) if pd.notna(row.get('Sector3Time')) else None,
            sector_1_session_time=str(row['Sector1SessionTime']) if pd.notna(row.get('Sector1SessionTime')) else None,
            sector_2_session_time=str(row['Sector2SessionTime']) if pd.notna(row.get('Sector2SessionTime')) else None,
            sector_3_session_time=str(row['Sector3SessionTime']) if pd.notna(row.get('Sector3SessionTime')) else None,
            speed_i1=float(row['SpeedI1']) if pd.notna(row.get('SpeedI1')) else None,
            speed_i2=float(row['SpeedI2']) if pd.notna(row.get('SpeedI2')) else None,
            speed_fl=float(row['SpeedFL']) if pd.notna(row.get('SpeedFL')) else None,
            speed_st=float(row['SpeedST']) if pd.notna(row.get('SpeedST')) else None,
            is_personal_best=bool(row['IsPersonalBest']) if pd.notna(row.get('IsPersonalBest')) else None,
            compound=str(row['Compound']) if pd.notna(row.get('Compound')) else None,
            tyre_life=float(row['TyreLife']) if pd.notna(row.get('TyreLife')) else None,
            fresh_tyre=bool(row['FreshTyre']) if pd.notna(row.get('FreshTyre')) else None,
            team=str(row['Team']) if pd.notna(row.get('Team')) else None,
            lap_start_time=str(row['LapStartTime']) if pd.notna(row.get('LapStartTime')) else None,
            lap_start_date=str(row['LapStartDate']) if pd.notna(row.get('LapStartDate')) else None,
            track_status=str(row['TrackStatus']) if pd.notna(row.get('TrackStatus')) else None,
            position=float(row['Position']) if pd.notna(row.get('Position')) else None,
            deleted=bool(row['Deleted']) if pd.notna(row.get('Deleted')) else None,
            deleted_reason=str(row['DeletedReason']) if pd.notna(row.get('DeletedReason')) else None,
            fast_f1_generated=bool(row['FastF1Generated']) if pd.notna(row.get('FastF1Generated')) else None,
            is_accurate=bool(row['IsAccurate']) if pd.notna(row.get('IsAccurate')) else None,
        )
  • server.py:37-83 (registration)
    Import statement in server.py that brings get_laps into scope for registration.
    from tools import (
        # ========================================
        # CORE SESSION DATA
        # ========================================
        get_session_details,          # Complete session overview
        get_session_results,           # Final classification
        get_laps,                      # Lap-by-lap data
        get_session_drivers,           # Driver list
        get_tire_strategy,             # Tire compounds and stints
        get_qualifying_sessions,       # Q1/Q2/Q3 splits
        get_track_evolution,           # Lap time progression
    
        # ========================================
        # TELEMETRY & ANALYSIS
        # ========================================
        get_lap_telemetry,             # High-frequency telemetry data
        compare_driver_telemetry,      # Side-by-side comparison
        get_analysis,                  # Race pace, tire deg, stints
        get_session_weather,           # Historical weather data
    
        # ========================================
        # RACE CONTROL & TRACK
        # ========================================
        get_race_control_messages,     # Flags, penalties, investigations, safety car
        get_circuit,                   # Circuit layout and corners
    
        # ========================================
        # LIVE TIMING - OpenF1
        # ========================================
        get_driver_radio,              # Team radio messages
        get_live_pit_stops,            # Pit stop timing
        get_live_intervals,            # Gaps and intervals
        get_meeting_info,              # Session schedule
        get_stints_live,               # Real-time tire stints
    
        # ========================================
        # CHAMPIONSHIP & SCHEDULE
        # ========================================
        get_standings,                 # Driver/constructor standings
        get_schedule,                  # F1 calendar
    
        # ========================================
        # REFERENCE & MEDIA
        # ========================================
        get_reference_data,            # Drivers, teams, circuits, tires
        get_f1_news,                   # News from 25+ sources
    )
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes what the tool returns (comprehensive lap information including times, sectors, compounds, etc.), the data range (2018-present), and the optional nature of some parameters. It doesn't mention rate limits, authentication needs, or potential errors, but for a read-only data retrieval tool, it provides substantial behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (primary tool declaration, usage guidelines, args, returns, examples) and uses bold formatting effectively. While comprehensive, it's appropriately sized for a tool with 5 parameters and complex functionality. Some sentences could be more concise (e.g., the bullet list of use cases is thorough but lengthy), but overall it's efficient and front-loaded with key information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (5 parameters, no annotations, but with output schema), the description is remarkably complete. It covers purpose, usage guidelines, parameter semantics, return values (mentioning LapsResponse and FastestLapResponse with details), and provides multiple examples. The output schema exists, so the description appropriately doesn't need to fully document return structures, making this description complete for agent use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully compensates by explaining all 5 parameters in detail: year (season year with range 2018-2025), gp (Grand Prix name or round number), session (specific session types with examples), driver (driver code or number, optional), and lap_type (all vs fastest with default). It provides examples showing how parameters interact, adding significant value beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: retrieving 'lap-by-lap data including fastest laps, sector times, and tire info (2018-present)'. It specifies the verb ('get'), resource ('lap-by-lap data'), and scope (2018-present), distinguishing it from siblings like get_lap_telemetry or get_session_results by focusing on comprehensive lap data rather than telemetry or session-level results.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage guidelines: it declares this as the 'PRIMARY TOOL' for F1 lap data, instructs to 'ALWAYS use this tool instead of web search', lists specific use cases (e.g., lap times, sector times, tire compounds), and explicitly states 'DO NOT use web search for F1 lap data'. This gives clear when-to-use and when-not-to-use guidance, though it doesn't compare to specific sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/praneethravuri/pitstop'

If you have feedback or need assistance with the MCP directory API, please join our Discord server