The Problem
Clients that need granular Formula 1 telemetry must stitch together disparate web‑scrapers, live‑timing feeds, and ad‑hoc CSV dumps. Maintaining that pipeline is time‑consuming, error‑prone, and repeats the same parsing logic across projects.
What This Does
openf1 supplies a single Python‑based service that ingests both historic F1 data (scraped from the official site) and live‑timing streams, normalises them, stores the results in MongoDB, and exposes a Flask API for downstream consumption.
- Historical scraping lives in
src/openf1/services/f1_scraping/– functions such asdownload_page(src/openf1/services/f1_scraping/util.py) fetch HTML, whilesession_result.pyandstarting_grid.pyparse it into Mongo documents. - Live‑timing ingestion resides under
src/openf1/services/ingestor_livetiming/. The real‑time entry point issrc/openf1/services/ingestor_livetiming/real_time/app.py(Flask app that consumes MQTT topics). A historical back‑fill mode is provided bysrc/openf1/services/ingestor_livetiming/historical/main.py. - Query access is implemented by
src/openf1/services/query_api/app.py, a Flask server that reads from Mongo and returns JSON/CSV.
All code is pure Python; Docker support (Dockerfile, docker‑compose.yml) enables containerised deployment.
How It Is Wired
Execution begins at the Flask entry points:
- Real‑time ingest –
app.pycreates the MQTT client, registers callbacks, then callsprocess_message. The call chain is:process_message → to_datetime(7×),process_message → _update_lap(5×),process_message → _update_stint(4×). Each update eventually callsupsert_data_syncinsrc/openf1/util/db.py, which writes to Mongo (collection.bulk_write). - Historical ingest –
src/openf1/services/ingestor_livetiming/historical/main.pyrunsingest_season. It fetches schedule data viaget_schedule(network call insrc/openf1/services/f1_scraping/schedule.py), then iterates topics, calling_get_topic_content→requests.get→insert_data_async. - API server –
src/openf1/services/query_api/app.pydefinesendpoint. A request hits_process_request, which parses the path (_parse_path), builds a Mongo query (query_params.py), and finally callsget_documentsinsrc/openf1/util/db.py. The response may also request a favicon via_get_favicon(outbound HTTP).
Core utilities that see the most traffic are src/openf1/util/db.py (15 functions, DB I/O) and src/openf1/util/misc.py (helpers like join_url). The import graph shows no circular dependencies, so changes to a hub file (e.g., objects.py with 16 functions) have a predictable blast radius.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/openf1.git
cd openf1
# Install in editable mode
pip install -e .
# Set Mongo connection (required for all services)
export MONGO_CONNECTION_STRING="mongodb://localhost:27017"
# Run the historical scraper (example for 2023 season)
python -m src.openf1.services.ingestor_livetiming.historical.main ingest_season 2023
# Start the live‑timing ingest
python -m src.openf1.services.ingestor_livetiming.real_time.app
# Launch the query API
python -m src.openf1.services.query_api.app
Docker alternative (build and start all three services) is defined in docker-compose.yml; the Dockerfile builds the Python environment.
Real‑World Use
A data‑science team can schedule ingest_season nightly to back‑fill a MongoDB cache, run the live‑timing ingest continuously during race weekends, and point their analytics notebooks at http://localhost:8000/v1/laps?... to retrieve clean lap‑by‑lap telemetry without writing custom scrapers.
Code Health & Issues
- High – GitHub Actions pinned to tags only (
JamesIves/github-pages-deploy-action@v4). Replace with commit SHA. - High – No test suite; repository contains 47 source files but zero tests. Add unit/integration tests for each public entry point.
- High – No lockfile for Python dependencies (
pyproject.tomlonly). Generate and commitpoetry.lockorrequirements.txtwith hashes. - High – Flask CORS is configured with
allow_origins=["*"]together with credentials. Restrict to explicit origins. - Medium – GitHub token permissions are not declared; add
permissions:block. - Medium – Dependabot/Renovate not configured; add
.github/dependabot.yml. - Medium – Docker base image
python:3.10-slimis unpinned; use digest. - Medium – No CI step for dependency vulnerability scanning; add
dependency-review-actionorosv-scanner. - Medium – Outbound
requests.getcalls lack timeouts; passtimeout=. - Medium –
actions/checkoutkeeps credentials; setpersist-credentials: false.
Additional observations: the repository includes a CI workflow (.github/workflows/tests.yml) but no test files, and the license file is present. No secrets are committed.
The Bottom Line
openf1 delivers a functional end‑to‑end pipeline for F1 data ingestion and API delivery, with clear separation between scraping, live‑timing, and query layers. However, the lack of tests, an unlocked dependency set, and several security‑related CI misconfigurations make it unsuitable for production without remediation. It is a solid starting point for internal tooling or research prototypes, provided the team adds test coverage and tightens the CI/CD hygiene.