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
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes | ||
| gp | Yes | ||
| session | Yes | ||
| driver | No | ||
| lap_type | No | all |
Implementation Reference
- tools/session/laps.py:47-120 (handler)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) )
- models/sessions/laps.py:1-59 (schema)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)
- tools/session/laps.py:10-44 (helper)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 )