Fast, pythonic access to MotoGP data — inspired by FastF1
Project description
PyMotoGP
Fast, pythonic access to MotoGP data — inspired by FastF1.
PyMotoGP gives you session-centric access to qualifying, sprint, and race data from the official PulseLive API and DORNA timing PDFs. Load a session, get a pandas DataFrame of laps, plot pace, run analytics, predict race outcomes.
import motogp
session = motogp.load(2024, 'qatar', 'Q2')
print(session.best_lap.rider_name) # 'Jorge MARTIN'
df = session.laps.to_dataframe() # full lap data, sectors, top speeds
session.plot.lap_times() # matplotlib figure
session.analysis.gap_to_pole() # gap analysis
Install
pip install pymotogp
Requires Python 3.10+. Core dependencies: requests, pandas, pdfplumber,
matplotlib.
Quickstart
Load a session
import motogp
# Year + event + session label. Event matches by name, country, or short code.
session = motogp.load(2024, 'cataluña', 'Q2')
Supported session labels: Q1, Q2, qualifying, FP, practice,
sprint, warm-up, race.
Inspect laps
df = session.laps.to_dataframe()
# Columns: rider_name, lap_number, lap_time_ms, sector1_ms ... sector4_ms,
# top_speed, is_valid, is_cancelled, is_pit, is_best
session.best_lap # fastest valid lap
session.riders # list of rider names
session.classification # official position list
Plot
fig = session.plot.lap_times(riders=['Bagnaia', 'Martin'])
fig.savefig('out.png')
session.plot.pace_distribution() # boxplot per rider
session.plot.sector_comparison() # grouped bars
session.plot.compare('Bagnaia', 'Martin') # 2-panel comparison
All plotting methods return a matplotlib.figure.Figure. Cancelled laps,
pit laps, and likely outlaps (>1.15× rider median) are filtered by default.
Analyze
session.analysis.theoretical_best() # sum of best sectors per rider
session.analysis.gain_potential('Bagnaia') # ms left on the table
session.analysis.gap_to_pole()
session.analysis.sector_strength()
session.analysis.consistency_ranking()
Cross-season history
from motogp import HistoricalAnalyzer
hist = HistoricalAnalyzer()
hist.track_evolution('catalunya') # pole-time progression by year
hist.rider_form('Bagnaia', year=2024) # per-round qualifying form
hist.team_pace(year=2024) # aggregate by team
Predict race pace (transparent baseline)
from motogp import RacePaceEstimator
qual = motogp.load(2024, 'malaysia', 'Q2')
est = RacePaceEstimator()
est.predict(qual, n_laps=20)
The estimator uses a transparent linear model:
race_lap(n) = q_best + race_offset + degradation × (n − 1). All assumptions
are exposed as parameters. See "Honest limits" below for accuracy data.
Data sources
PyMotoGP uses a two-tier resolution strategy:
- Local cache (instant) — if you have a directory of pre-scraped JSON
files, set
MOTOGP_SCRAPER_OUTPUTto point at it. Hits return instantly. - PulseLive API (live) — falls back to the official MotoGP API:
api.motogp.pulselive.com/motogp/v1/. Discovers the session, downloads the official Analysis PDF, parses it withpdfplumber. PDFs are cached at~/.motogp_pdfs/.
No API key required. Be considerate — the library caches everything.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
MOTOGP_SCRAPER_OUTPUT |
~/.motogp/scraper_output |
Pre-scraped JSON cache |
MOTOGP_PDF_CACHE |
~/.motogp_pdfs |
DORNA Analysis PDF cache |
Honest limits
PyMotoGP includes a backtest pipeline so you can measure model accuracy instead of trusting it blindly.
from motogp.analysis import RacePaceValidator
v = RacePaceValidator()
df = v.validate_season(2024)
v.summarize(df)
2024 season backtest results (20 GP races validated):
| Metric | Value |
|---|---|
| Winner hit rate | 30% |
| Podium overlap (mean of 3) | 1.2 |
| Position MAE | 3.5 places |
| Kendall's tau | 0.24 |
Treat RacePaceEstimator.predict() as a directional baseline, not a
black-box predictor. The model assumes qualifying pace transfers linearly
to race pace with uniform degradation — which is wrong for ~70% of MotoGP
races because tire management, race craft, weather, and DNFs aren't
captured. Calibrate per-track with est.calibrate_from_race(qual, race)
once you have real race data.
Architecture
motogp/
├── core/ # Session, Lap, Sector, Rider data models
├── api/ # PulseLive client + DORNA PDF parser
├── plots/ # Matplotlib-based session plots
├── analysis/ # SessionAnalyzer, HistoricalAnalyzer,
│ # RacePaceEstimator, RacePaceValidator
Roadmap
- ✅ Phase 1 — PulseLive API wrapper, core data models, caching
- ✅ Phase 2 — Real data integration (scraper cache + PulseLive + PDFs)
- ✅ Phase 3 — Session/historical/race-pace analytics + validation
- 🚧 Phase 4 — Sphinx docs, VCR-cassette integration tests, PyPI release
Contributing
Issues and PRs welcome. Run tests with:
pip install -e ".[dev]"
pytest
License
Not affiliated with Dorna Sports or MotoGP™. All data is sourced from publicly available APIs and PDFs for personal and research use.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymotogp-0.1.0.tar.gz.
File metadata
- Download URL: pymotogp-0.1.0.tar.gz
- Upload date:
- Size: 50.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
643b243a43b33027520a74262a77e5ade70c7a6c77abe23be42bff911da7af8f
|
|
| MD5 |
26efbb16dd68593ddac09ab79aa4be99
|
|
| BLAKE2b-256 |
367be9099209fd71b562930c4887f0ecaf7caeddc56e942df75ea771f6557c1f
|
Provenance
The following attestation bundles were made for pymotogp-0.1.0.tar.gz:
Publisher:
release.yml on tejred213/PyMotoGP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymotogp-0.1.0.tar.gz -
Subject digest:
643b243a43b33027520a74262a77e5ade70c7a6c77abe23be42bff911da7af8f - Sigstore transparency entry: 1553716658
- Sigstore integration time:
-
Permalink:
tejred213/PyMotoGP@5edba3b86d9f9344dd6a98ba4fa55fbb658601ca -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tejred213
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5edba3b86d9f9344dd6a98ba4fa55fbb658601ca -
Trigger Event:
push
-
Statement type:
File details
Details for the file pymotogp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pymotogp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 42.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cda334029a5febc2540115be6a3be8c78af28a22989d566963041244efd7d931
|
|
| MD5 |
4db1347f2760e4fa1fa7ffd220a47bf7
|
|
| BLAKE2b-256 |
8adc57eda625522eb1ea3e046bd85a7dc8f2ef31a8565bbf80ccefef309efa8d
|
Provenance
The following attestation bundles were made for pymotogp-0.1.0-py3-none-any.whl:
Publisher:
release.yml on tejred213/PyMotoGP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymotogp-0.1.0-py3-none-any.whl -
Subject digest:
cda334029a5febc2540115be6a3be8c78af28a22989d566963041244efd7d931 - Sigstore transparency entry: 1553716667
- Sigstore integration time:
-
Permalink:
tejred213/PyMotoGP@5edba3b86d9f9344dd6a98ba4fa55fbb658601ca -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tejred213
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5edba3b86d9f9344dd6a98ba4fa55fbb658601ca -
Trigger Event:
push
-
Statement type: