Historical data cached forever to disk. Current season cached 5 min. Never fetches the same historical data twice — even across restarts.
TypeScript SDK
f1.js
Formula 1 data for Node.js developers
Full race results, standings, and live telemetry — 1950 to present.
Zero config. Zero dependencies. Typed, cached, rate-limited.
/ 01 — Quick start
Three lines
to race data
import { f1 } from '@paraspatankar/f1js' // Get every race result from the 2024 Bahrain GP const results = await f1.races.getResults(2024, 1) // → Race[], fully typed. Cached forever (historical data). // Who won? const winner = results.find(r => r.position === 1) console.log(winner?.driverId) // → "verstappen" // Full season schedule const races = await f1.races.getSeason(2023) console.log(races.length) // → 22
import { f1 } from '@paraspatankar/f1js' // Verstappen's full telemetry trace — Bahrain 2024 // session_key resolved automatically from season + round const carData = await f1.telemetry.getCarData(2024, 1, 'VER') // → 34,819 CarData samples. Cached on first fetch. // Top speed at Bahrain const topSpeed = Math.max(...carData.map(d => d.speed)) console.log(topSpeed) // → 342 (km/h) // Tyre strategy const stints = await f1.telemetry.getStints(2024, 1, 'HAM') stints.forEach(s => console.log(`${s.compound} — laps ${s.lapStart}–${s.lapEnd}`)
import { f1 } from '@paraspatankar/f1js' // Parse a lap time string into milliseconds f1.utils.parseLapTime('1:23.456') // → 83456 // Format milliseconds back to M:SS.mmm f1.utils.formatLapTime(83456) // → '1:23.456' // Calculate the gap between two laps f1.utils.lapGap('1:23.456', '1:23.891') // → '+0.435'
/ 02 — Architecture
Everything
handled for you
Built-in sliding-window rate limiter keeps calls under OpenF1's 3 req/s ceiling. You call the method; the library queues everything safely.
Pass season + round. The library maps it to OpenF1's session_key automatically. You never deal with two APIs' ID systems.
Jolpica caps responses at 100. fetchAll() loops transparently so you get the complete dataset in one call, every time.
Every method, parameter, and return type is strictly typed. Ships .d.ts declarations. Works in CJS and ESM projects.
Cache, rate limiter, HTTP client — all Node.js builtins. No supply chain risk. npm install and you're done.
.getCarData()
hit / miss
sliding window
+ OpenF1
objects
CarData[]…
/ 03 — API Reference
9 methods.
All of F1.
| Namespace | Method | Returns | Source | Coverage |
|---|---|---|---|---|
| races | getSeason(season) | Race[] — full calendar | Jolpica | 1950–present |
| races | getResults(season, round) | RaceResult[] — full grid | Jolpica | 1950–present |
| drivers | getSeason(season) | Driver[] — all drivers | Jolpica | 1950–present |
| drivers | getDriver(driverId) | Driver — single record | Jolpica | 1950–present |
| standings | getDriverStandings(season) | DriverStanding[] | Jolpica | 1950–present |
| standings | getConstructorStandings(season) | ConstructorStanding[] | Jolpica | 1950–present |
| telemetry | getCarData(season, round, driver) | CarData[] — speed/throttle/brake/RPM/gear/DRS | OpenF1 | 2023–present |
| telemetry | getStints(season, round, driver?) | Stint[] — compound, lap range | OpenF1 | 2023–present |
| telemetry | getWeather(season, round) | WeatherSample[] — temp/humidity/wind | OpenF1 | 2023–present |
/ 04 — Limitations
Honest about
the boundaries
Telemetry is 2023+ only
OpenF1 only covers 2023 onwards. Race results and standings go back to 1950 via Jolpica.
Node.js only (v1)
The filesystem cache uses Node's fs/promises. Browser support is planned for v2 with an isomorphic cache adapter.
No live race data
This version targets historical and post-session data. A f1.live.* namespace with event-driven polling is on the v2 roadmap.
First telemetry fetch is slow
~35,000 samples per driver race. Rate limiter queues everything safely. Second run: instant (filesystem cache hit).