Skip to content
All workCase study / API and dashboard

Race review / API integration

pitwall

The race, brought together.

Official results, lap times and tyre strategy on one page, with a clear account of where every part came from.

pitwall dashboard for the 2024 Belgian Grand Prix, with the podium and the top of the classification table
Belgian Grand Prix 2024 · actual dashboard
2independent data sources
86race sessions matched
107offline tests

Matching checked from 2023 through round 16 of 2026.

I wanted the result of a Formula 1 race, its tyre strategy and its lap times on one page. The data was available, but split between two APIs that identify races and drivers differently. I built pitwall to line them up and keep showing useful information when one source is unavailable.

The problem

Jolpica-F1 supplies the official classification, points and standings. OpenF1 supplies lap times, tyre stints, pit stops and weather. Joining them means working out which session belongs to a race and which driver each timing record describes.

The sources also fail differently. OpenF1 restricts free access while a session is live, and either API can time out or throttle requests. I wanted the official result to remain available even when the timing could not be fetched.

What I built

A FastAPI service fetches both sources, normalises their responses and stores them in SQLite. The dashboard shows the classification, tyre strategy, lap times and championship after the selected round.

Each API has its own HTTP client and rate limiter. Requests retry timeouts, throttling and server errors with backoff, and respect Retry-After when the source supplies it. Requests arriving together for the same data share one upstream fetch.

The cache can return the last good response if a refresh fails. Every view carries source status, so the dashboard can say whether a section came from a fresh fetch, the cache, an older copy or no available data.

Tyre strategy chart with one bar per driver, split into stints by compound
Tyre strategy chart with one bar per driver, split into stints by compound

The join needed more than a shared ID

Race matching uses the nearest OpenF1 race session within 36 hours, rather than assuming both sources give the same calendar date. Las Vegas 2024 is one example: pairing a local race date with a UTC time can put the start a day early. OpenF1 supplies the actual start time.

Drivers are matched by car number first, then by three-letter code. Anything left unmatched is included in the report's notes. For the official result, status text takes precedence over a numeric position: a disqualified driver can still have a position and a misleadingly fast race time in older data.

Missing data gets its own explanation. OpenF1 has no timing before 2023 and no pit stops before Spain that year. An empty pit-stop list should not imply that nobody stopped.

Making the charts usable

The tyre chart puts each driver on one row, with a letter inside every stint so colour is not the only way to identify the compound. The lap chart lets me compare up to three drivers against the rest of the field.

Both charts have table views and arrow-key controls. I also checked chart colours for contrast and colour-blindness in the light and dark themes.

What I checked

I ran the source matching across 86 races from 2023 through round 16 of 2026. Each race found its OpenF1 session. One driver remained unmatched: Stroll at Singapore 2023, where he withdrew after a qualifying crash.

The repository includes 107 offline tests using a trimmed copy of the 2024 Belgian Grand Prix data, plus fake network responses and a controllable clock. That lets the tests exercise cache expiry, retries and missing-source behaviour without depending on the APIs being up.

What it doesn't do

  • It is a race review dashboard. Free access restrictions still apply while a session is live.
  • A cached response can be old. The dashboard labels it instead of presenting it as fresh data.
  • Timing coverage starts in 2023, and older races can have missing fields.
  • Matching every race session does not establish that every upstream field is correct.

Have a similar workflow?

I build API integrations and dashboards for businesses that need several sources in one place, with a clear account of what's current and what's missing. Write to me at luka@puhmeister.dev.