Historical API

Public, no-authentication REST endpoints for F1 race data — results, standings, lap times, tyre stints, pit stops, and qualifying laps. Free to use, no API key required.

No authentication required. All Historical API endpoints are public. No Authorization header, no token, no sign-up needed.
Base URL
https://api.racehooks.io/v1/historical/
Auth
None
Cache
public, max-age=3600
Pagination
?limit= (max 100) · ?offset=

Response envelope

Every response wraps its payload in an RHData object. All scalar values — numbers, years, positions, milliseconds — are serialised as strings.

FieldTypeDescription
RHData.seriesstringAlways "f1"
RHData.urlstringCanonical URL of this request
RHData.limitstringPage size used (serialised as string)
RHData.offsetstringPage offset used (serialised as string)
RHData.totalstringTotal records matching the query (serialised as string)
RHData.<Table>objectData payload — SeasonTable, RaceTable, DriverTable, ConstructorTable, CircuitTable, or StandingsTable
Time formats: Lap and sector times use M:SS.mmm (or SS.mmm if under 1 minute). Race winner absolute time: H:MM:SS.mmm. Gap to leader: +S.mmm.

Quickstart

curl https://api.racehooks.io/v1/historical/seasons

Data coverage

FieldTypeDescription
dataQualityTier"1"–"4"1=1950–94 (Wikipedia only) · 2=1995–2017 · 3=2018–22 · 4=2023+ (full telemetry ingest)
Laps / Stints2019+Lap times, sector splits, tyre compounds, and stints require per-session ingest. Pre-2019 sessions return empty arrays.
QualifyingLaps2019+Flying laps from Q1/Q2/Q3 (and sprint qualifying SQ1/SQ2/SQ3). Empty arrays for pre-2019 sessions.
FastestLapvariesLap number, time, and average speed when available. Rank is not stored.
PitStop.timeoptionalTime-of-day of pit entry — omitted when not stored.
carNumberoptionalDerived from driver's current number. Omitted for pre-modern seasons or drivers without a permanent number.

Pagination

All list endpoints support ?limit= (max 100) and ?offset=. Lap time, pit stop, stint, and qualifying lap endpoints default to limit=100; all others default to limit=30. The total field in the envelope reflects the count before pagination.

Note on lap pagination: /laps paginates by lap number, not by row. Each page contains complete timing data for all drivers on those laps.

Seasons

List seasons

GET/v1/historical/seasons

Returns all seasons in the database with the number of rounds per season.

json
{
  "RHData": {
    "series": "f1",
    "url": "https://api.racehooks.io/v1/historical/seasons",
    "limit": "30",
    "offset": "0",
    "total": "1",
    "SeasonTable": {
      "Seasons": [
        { "season": "2025", "rounds": "2" }
      ]
    }
  }
}

Race calendar

Season race calendar

GET/v1/historical/:season/races

Race calendar for a season: round number, race name, date, official name, and circuit location.

FieldTypeDescription
:seasonintegerSeason year, e.g. 2025
json
{
  "RHData": {
    "series": "f1",
    "url": "https://api.racehooks.io/v1/historical/2025/races",
    "limit": "30",
    "offset": "0",
    "total": "2",
    "RaceTable": {
      "season": "2025",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "Circuit": {
            "circuitId": "bahrain",
            "circuitName": "Bahrain International Circuit",
            "Location": { "lat": "26.0325", "long": "50.5106", "locality": "Sakhir", "country": "Bahrain" }
          },
          "date": "2025-03-16",
          "officialName": "FORMULA 1 ARAMCO BAHRAIN GRAND PRIX 2025",
          "dataQualityTier": "4"
        }
      ]
    }
  }
}

Race results

Race results

GET/v1/historical/:season/:round/results

Finish order, points, grid position, race time/gap, and fastest lap per driver.

FieldTypeDescription
:seasonintegerSeason year
:roundintegerRound number within the season
?limitintegerPage size (default 30, max 100)
?offsetintegerPage offset
{
  "RHData": {
    "series": "f1",
    "url": "https://api.racehooks.io/v1/historical/2025/1/results",
    "limit": "30",
    "offset": "0",
    "total": "3",
    "RaceTable": {
      "season": "2025",
      "round": "1",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "Circuit": { "circuitId": "bahrain", "circuitName": "Bahrain International Circuit", "Location": { "lat": "26.0325", "long": "50.5106", "locality": "Sakhir", "country": "Bahrain" } },
          "date": "2025-03-16",
          "Results": [
            {
              "position": "1",
              "positionText": "1",
              "points": "25",
              "number": "1",
              "Driver": { "driverId": "verstappen-max", "givenName": "Max", "familyName": "Verstappen", "permanentNumber": "1", "code": "VER", "dateOfBirth": "1997-09-30", "nationality": "Dutch", "activeFromYear": "2015" },
              "Constructor": { "constructorId": "red-bull-racing", "name": "Red Bull Racing", "nationality": "Austrian", "lineageId": "red-bull-racing", "activeFromYear": "2005" },
              "grid": "1",
              "laps": "57",
              "status": "Finished",
              "Time": { "millis": "5523456", "time": "1:32:03.456" }
            },
            {
              "position": "2",
              "positionText": "2",
              "points": "18",
              "number": "4",
              "Driver": { "driverId": "norris-lando", "givenName": "Lando", "familyName": "Norris" },
              "Constructor": { "constructorId": "mclaren", "name": "McLaren" },
              "grid": "3",
              "laps": "57",
              "status": "Finished",
              "Time": { "time": "+12.345" }
            }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
positionstringFinishing position (numeric), or empty string for non-classified
positionTextstringPosition text — mirrors position for classified drivers
pointsstringChampionship points scored
numberstringCar number — omitted when unavailable (pre-modern seasons)
gridstringStarting grid position
lapsstringLaps completed
statusstringFinished | DNF | DSQ | DNS
Time.millisstringRace winner only: absolute time in milliseconds
Time.timestringWinner: H:MM:SS.mmm · Others: +S.mmm gap to leader
FastestLapobjectLap number, time, and average speed — omitted when unavailable

Qualifying

Qualifying results

GET/v1/historical/:season/:round/qualifying

Grid position and Q1/Q2/Q3 session times per driver.

FieldTypeDescription
:seasonintegerSeason year
:roundintegerRound number
?limitintegerPage size (default 30, max 100)
?offsetintegerPage offset
json
{
  "RHData": {
    "RaceTable": {
      "season": "2025",
      "round": "1",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "QualifyingResults": [
            {
              "position": "1",
              "Driver": { "driverId": "verstappen-max", "givenName": "Max", "familyName": "Verstappen", "code": "VER" },
              "Constructor": { "constructorId": "red-bull-racing", "name": "Red Bull Racing" },
              "Q1": "1:30.100",
              "Q2": "1:29.500",
              "Q3": "1:28.979"
            },
            {
              "position": "2",
              "Driver": { "driverId": "leclerc-charles", "givenName": "Charles", "familyName": "Leclerc", "code": "LEC" },
              "Constructor": { "constructorId": "ferrari", "name": "Ferrari" },
              "Q1": "1:30.400",
              "Q2": "1:29.700",
              "Q3": "1:29.124"
            }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
positionstringQualifying position
Q1stringBest Q1 lap time in M:SS.mmm format
Q2stringBest Q2 lap time — omitted if driver did not participate
Q3stringBest Q3 lap time — omitted if driver did not participate

Qualifying lap times

GET/v1/historical/:season/:round/qualifying/laps2019+

Individual flying laps from Q1/Q2/Q3 (and sprint qualifying SQ1/SQ2/SQ3) with sector splits, tyre compound, and tyre age. Returns empty when per-session data is not available (pre-2019).

FieldTypeDescription
:seasonintegerSeason year
:roundintegerRound number
?driverstringFilter by driver slug (e.g. verstappen-max)
?sessionstringFilter by session key: Q1, Q2, Q3, SQ1, SQ2, SQ3
?limitintegerPage size (default 100, max 100)
?offsetintegerPage offset
json
{
  "RHData": {
    "RaceTable": {
      "season": "2025",
      "round": "1",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "QualifyingLaps": [
            {
              "driverId": "norris-lando",
              "session": "Q3",
              "lapNumber": "1",
              "time": "1:29.200",
              "compound": "SOFT",
              "tyreAgeLaps": "1"
            },
            {
              "driverId": "verstappen-max",
              "session": "Q3",
              "lapNumber": "1",
              "time": "1:28.979",
              "sector1": "26.500",
              "sector2": "32.200",
              "sector3": "30.279",
              "compound": "SOFT",
              "tyreAgeLaps": "1"
            }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
driverIdstringDriver slug
sessionstringSession key (Q1, Q2, Q3, SQ1, SQ2, SQ3)
lapNumberstringLap number within this qualifying session
timestringLap time in M:SS.mmm format
sector1/2/3stringSector times — omitted when unavailable
compoundstringTyre compound: SOFT, MEDIUM, HARD, INTER, WET — omitted when unavailable
tyreAgeLapsstringLaps on this set of tyres before this lap — omitted when unavailable
trackStatusstringF1 track status code (1=clear, 2=yellow, etc.) — omitted when unavailable

Lap times

Lap times

GET/v1/historical/:season/:round/laps2019+

Per-lap timing data for all drivers, grouped by lap number. Includes sector splits, tyre compound, tyre life, gap to leader, and track status. Returns empty for pre-2019 sessions.

FieldTypeDescription
:seasonintegerSeason year
:roundintegerRound number
?driverstringFilter to a single driver slug
?limitintegerNumber of lap numbers to return (default 100, max 100)
?offsetintegerLap number offset (paginates by distinct lap, not by row)
json
{
  "RHData": {
    "series": "f1",
    "url": "https://api.racehooks.io/v1/historical/2025/1/laps?limit=1",
    "limit": "1",
    "offset": "0",
    "total": "2",
    "RaceTable": {
      "season": "2025",
      "round": "1",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "Laps": [
            {
              "number": "1",
              "Timings": [
                {
                  "driverId": "verstappen-max",
                  "position": "1",
                  "time": "1:36.500",
                  "sector1": "28.500",
                  "sector2": "35.000",
                  "sector3": "33.000",
                  "compound": "SOFT",
                  "tyreLife": "1",
                  "trackStatus": "1"
                },
                {
                  "driverId": "norris-lando",
                  "position": "2",
                  "time": "1:37.100",
                  "compound": "SOFT",
                  "tyreLife": "1",
                  "gapToLeader": "+0.600"
                }
              ]
            }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
Laps[].numberstringLap number
Timings[].driverIdstringDriver slug
Timings[].positionstringRace position at end of this lap
Timings[].timestringLap time in M:SS.mmm format
Timings[].sector1/2/3stringSector times — omitted when unavailable
Timings[].compoundstringTyre compound — omitted when unavailable
Timings[].tyreLifestringLaps on current set (1 = first lap on these tyres)
Timings[].isPitOutLap"true"Present only on pit-out laps
Timings[].trackStatusstringF1 track status code — omitted when unavailable
Timings[].gapToLeaderstring+S.mmm gap to race leader — omitted when unavailable
Timings[].gapToCarAheadstring+S.mmm gap to the car immediately ahead — omitted when unavailable
# All laps for a driver
curl "https://api.racehooks.io/v1/historical/2025/1/laps?driver=verstappen-max"

# Specific lap range
curl "https://api.racehooks.io/v1/historical/2025/1/laps?offset=20&limit=10"

Pit stops

Pit stops

GET/v1/historical/:season/:round/pitstops

All pit stop records for a race: stop number, lap, duration. Optional driver filter.

FieldTypeDescription
:seasonintegerSeason year
:roundintegerRound number
?driverstringFilter to a single driver slug
?limitintegerPage size (default 100, max 100)
?offsetintegerPage offset
json
{
  "RHData": {
    "RaceTable": {
      "season": "2025",
      "round": "1",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "PitStops": [
            { "driverId": "verstappen-max", "stop": "1", "lap": "22", "duration": "22.100" },
            { "driverId": "norris-lando",   "stop": "1", "lap": "24", "duration": "23.400" }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
driverIdstringDriver slug
stopstringStop number for this driver (1-indexed)
lapstringLap on which the stop occurred
timestringTime of day of pit entry (H:MM:SS.mmm) — omitted when unavailable
durationstringStationary time in the pit box in M:SS.mmm format

Tyre stints

Tyre stints

GET/v1/historical/:season/:round/stints2019+

Tyre stint data per driver: stint number, lap range, compound, and tyre age at stint start. Returns empty for pre-2019 sessions.

FieldTypeDescription
:seasonintegerSeason year
:roundintegerRound number
?driverstringFilter to a single driver slug
?limitintegerPage size (default 100, max 100)
?offsetintegerPage offset
json
{
  "RHData": {
    "RaceTable": {
      "season": "2025",
      "round": "1",
      "Races": [
        {
          "season": "2025",
          "round": "1",
          "raceName": "Bahrain Grand Prix",
          "Stints": [
            { "driverId": "verstappen-max", "stint": "1", "lapStart": "1",  "lapEnd": "22", "compound": "SOFT",   "tyreAgeAtStart": "0" },
            { "driverId": "verstappen-max", "stint": "2", "lapStart": "23", "lapEnd": "57", "compound": "MEDIUM", "tyreAgeAtStart": "0" },
            { "driverId": "norris-lando",   "stint": "1", "lapStart": "1",  "lapEnd": "24", "compound": "SOFT",   "tyreAgeAtStart": "0" },
            { "driverId": "norris-lando",   "stint": "2", "lapStart": "25", "lapEnd": "57", "compound": "MEDIUM", "tyreAgeAtStart": "0" }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
driverIdstringDriver slug
stintstringStint number for this driver (1-indexed)
lapStartstringFirst lap of this stint
lapEndstringLast lap of this stint
compoundstringTyre compound: SOFT, MEDIUM, HARD, INTER, WET
tyreAgeAtStartstringLaps already on these tyres when the stint began (0 = new set)

Standings

Driver standings

GET/v1/historical/:season/driverStandings

Driver championship standings for a season. The wins count is computed dynamically from race results (not a stored column), so it always reflects the authoritative finish data.

FieldTypeDescription
:seasonintegerSeason year
?limitintegerPage size (default 30, max 100)
?offsetintegerPage offset
json
{
  "RHData": {
    "series": "f1",
    "url": "https://api.racehooks.io/v1/historical/2025/driverStandings",
    "limit": "30",
    "offset": "0",
    "total": "3",
    "StandingsTable": {
      "season": "2025",
      "StandingsLists": [
        {
          "season": "2025",
          "round": "2",
          "DriverStandings": [
            {
              "position": "1",
              "positionText": "1",
              "points": "43",
              "wins": "1",
              "Driver": { "driverId": "verstappen-max", "givenName": "Max", "familyName": "Verstappen", "permanentNumber": "1", "code": "VER", "nationality": "Dutch" },
              "Constructors": [{ "constructorId": "red-bull-racing", "name": "Red Bull Racing", "nationality": "Austrian" }]
            },
            {
              "position": "2",
              "positionText": "2",
              "points": "36",
              "wins": "0",
              "Driver": { "driverId": "norris-lando", "givenName": "Lando", "familyName": "Norris", "code": "NOR", "nationality": "British" },
              "Constructors": [{ "constructorId": "mclaren", "name": "McLaren", "nationality": "British" }]
            }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
StandingsLists[].roundstringLast round number included in this dataset
DriverStandings[].positionstringChampionship position
DriverStandings[].pointsstringPoints total
DriverStandings[].winsstringRace wins (computed from race results)
DriverStandings[].DriverobjectDriver object — see Driver reference
DriverStandings[].ConstructorsarrayArray containing the driver's current constructor

Constructor standings

GET/v1/historical/:season/constructorStandings

Constructor championship standings. Engine-suffix duplicates (e.g. McLaren-Mercedes and McLaren-Renault for the same lineage) are automatically collapsed into a single entry. Wins are aggregated across all constructor variants sharing a lineageId.

FieldTypeDescription
:seasonintegerSeason year
?limitintegerPage size (default 30, max 100)
?offsetintegerPage offset
json
{
  "RHData": {
    "StandingsTable": {
      "season": "2025",
      "StandingsLists": [
        {
          "season": "2025",
          "round": "2",
          "ConstructorStandings": [
            {
              "position": "1",
              "positionText": "1",
              "points": "43",
              "wins": "1",
              "Constructor": { "constructorId": "red-bull-racing", "name": "Red Bull Racing", "nationality": "Austrian", "lineageId": "red-bull-racing", "activeFromYear": "2005" }
            },
            {
              "position": "2",
              "positionText": "2",
              "points": "36",
              "wins": "0",
              "Constructor": { "constructorId": "mclaren", "name": "McLaren", "nationality": "British", "lineageId": "mclaren", "activeFromYear": "1966" }
            }
          ]
        }
      ]
    }
  }
}
FieldTypeDescription
ConstructorStandings[].winsstringRace wins (aggregated by lineageId across all engine variants)
Constructor.lineageIdstringStable identifier across engine-suffix name changes. Use this for cross-season queries.

Reference objects

Driver object

GET/v1/historical/drivers
GET/v1/historical/drivers/:driverId

Returns all drivers in the database, or a single driver by slug.

json
{
  "RHData": {
    "series": "f1",
    "url": "https://api.racehooks.io/v1/historical/drivers",
    "limit": "30",
    "offset": "0",
    "total": "3",
    "DriverTable": {
      "Drivers": [
        {
          "driverId": "leclerc-charles",
          "givenName": "Charles",
          "familyName": "Leclerc",
          "permanentNumber": "16",
          "code": "LEC",
          "dateOfBirth": "1997-10-16",
          "nationality": "Monégasque",
          "activeFromYear": "2018"
        },
        {
          "driverId": "norris-lando",
          "givenName": "Lando",
          "familyName": "Norris",
          "permanentNumber": "4",
          "code": "NOR",
          "dateOfBirth": "1999-11-13",
          "nationality": "British",
          "activeFromYear": "2019"
        }
      ]
    }
  }
}
FieldTypeDescription
driverIdstringRaceHooks driver slug (forename-surname, lowercase)
givenNamestringFirst name
familyNamestringLast name
permanentNumberstringPermanent car number
codestring3-letter TLA (e.g. VER)
dateOfBirthstringISO 8601 date (YYYY-MM-DD)
nationalitystringNationality
activeFromYearstringSeason of first race
activeToYearstringSeason of last race — omitted for active drivers

Constructor object

GET/v1/historical/constructors
GET/v1/historical/constructors/:constructorId
FieldTypeDescription
constructorIdstringRaceHooks constructor slug
namestringConstructor name as it appears in historical records
nationalitystringNationality
lineageIdstringStable lineage identifier — use this for cross-season queries
activeFromYearstringSeason of first race entry
activeToYearstringSeason of last race entry — omitted for active constructors

Circuit object

GET/v1/historical/circuits
GET/v1/historical/circuits/:circuitId
FieldTypeDescription
circuitIdstringRaceHooks circuit slug (e.g. bahrain, albert-park)
circuitNamestringFull circuit name
Location.latstringLatitude (decimal degrees)
Location.longstringLongitude (decimal degrees)
Location.localitystringCity or locality name
Location.countrystringCountry name

All routes

EndpointDescriptionDefault limitFilters
/historical/seasonsAll seasons with round count30
/historical/:season/racesSeason race calendar30
/historical/:season/:round/resultsRace results30
/historical/:season/:round/qualifyingQualifying results (Q1/Q2/Q3)30
/historical/:season/:round/qualifying/lapsQualifying flying laps100?driver, ?session
/historical/:season/:round/lapsPer-lap timings (by lap number)100?driver
/historical/:season/:round/pitstopsPit stop records100?driver
/historical/:season/:round/stintsTyre stint data100?driver
/historical/:season/driverStandingsDriver championship standings30
/historical/:season/constructorStandingsConstructor championship standings30
/historical/driversAll drivers30
/historical/drivers/:driverIdSingle driver by slug1
/historical/constructorsAll constructors30
/historical/constructors/:constructorIdSingle constructor by slug1
/historical/circuitsAll circuits30
/historical/circuits/:circuitIdSingle circuit by slug1