HYROX race data client – retrieve full race results via Python
Project description
pyrox-client
Unofficial Python client for HYROX race results — load public results into pandas DataFrames.
Load HYROX race results into pandas in a few lines. Built for people who love fitness and data.
Unofficial Python client for HYROX race results — load public results into pandas DataFrames in a few lines. Built for people who love fitness and data: analyse performance trends, understand HYROX’s unique demands, and open up new research avenues.
Documentation
- Live docs: https://vmatei2.github.io/pyrox-client/
- Local preview:
mkdocs serve
Install
uv pip install pyrox-client
or
pip install pyrox-client
Quickstart
Below we have added two quick examples. One of loading race data - with different data requests, and one of retrieving race data / extracting athlete information and plotting values with example outputs to show a small glimpse of the analysis possible if retrieving the race data.
import pyrox
# Create client
client = pyrox.PyroxClient()
# Discover available races
all_races = client.list_races()
s6_races = client.list_races(season=6)
# Get multiple races from a season
subset_s6 = client.get_season(season=6, locations=["london", "hamburg"])
# Get single race df
london_race = client.get_race(season=6, location="london")
rott_race = client.get_race(season=6, location="rotterdam")
london_male = client.get_race(season=6, location="london", gender="male")
# Filter athletes finishing under an hour (times parsed to minutes)
london_sub60 = client.get_race(season=6, location="london", total_time=60)
# Open interval: keep athletes with 50 < total_time < 60 minutes
london_50_60 = client.get_race(season=6, location="london", total_time=(50, 60))
# Returning data for May (London Olympia race)
london_2025_s7 = client.get_race(season=7, location="london", year=2025)
# Returning data for November (London Excel Race)
london_2024_s7 = client.get_race(season=7, location="london", year=2024)
What's included?
- Servers publicly available race results from the offical results website
- Historical coverage of Season 2-7 (for now) (season 5 and 6 are most used/tested in analysis; please open issues for any data problems spotted)
- Client-side caching by default (local). Set
use_cache=Falsewhen queryingget_race()orget_season()to opt out. - Read-only CDN data source – the client always fetches from the public Pyrox CloudFront bucket (no bucket configuration required or supported).
- Client-side helpers for gender, division, and total finish time filtering (single threshold or open interval)
API
list_races(season: int | None = None) -> pd.DataFrame
- returns a Dataframe of available races
from pyrox import PyroxClient
client = PyroxClient()
print(client.list_races(season=5).head(3))
# season location
# 0 5 amsterdam
# 1 5 anaheim
# 2 5 barcelona
get_race(
season: int,
location: str,
year: int | None = None,
gender: str | None = None, # "male" | "female" | "mixed"
division: str | None = None, # "open" | "pro" | "pro_doubles" (case-insensitive contains)
total_time: float | tuple[float | None, float | None] | None = None,
use_cache: bool = True,
) -> pd.DataFrame
- returns a single race as a pandas dataframe - with optional filtering.
total_timeoperates on minutes after parsing (e.g.total_time=60keeps athletes under an hour,total_time=(50, 60)keeps athletes between 50 and 60 minutes exclusive; useNonefor an open bound).
get_season(
season: int,
locations: list[str] | None = None,
use_cache: bool = True,
) -> pd.DataFrame
- returns a combinded Dataframe for a whole season (or a set of locations passed in)
get_athlete_in_race(
season: int,
location: str,
athlete_name: str,
year: Optional[int] = None,
gender: Optional[str] = None,
division: Optional[str] = None,
use_cache: bool = True,
)
- returns a pd Dataframe filtered down to the matching athlete
- matching is case-insensitive
- if more than one row matches, all rows are returned
- names are in the format shown on 'results.hyrox' i.e. (LastName, FirstName)
df = client.get_athlete_in_race(
season=7,
location="London",
year=2023,
gender="male",
division="open",
athlete_name="LastName, FirstName",
)
clear_cache(pattern: str = "*") -> None
- clears local cache entries (regex pattern search option included)
cache_info() -> dict
- returns cache statistics:
{"total_size": int, "total_items": int, "items": list[str]}
Example - compare an athlete's two races vs field averages
below code requires
pandasandnumpyand the graphs are generated viamatplotlibandseaborn
import pandas as pd
import numpy as np
import pyrox
client = pyrox.PyroxClient()
run_cols = [f"run{i}_time" for i in range(1, 8+1)]
station_cols = [
"skiErg_time","sledPush_time","sledPull_time","burpeeBroadJump_time",
"rowErg_time","farmersCarry_time","sandbagLunges_time","wallBalls_time",
]
station_labels = ["SkiErg","Sled Push","Sled Pull","BBJ","Row","Farmers","Lunges","Wall Balls"]
def pick_athlete_row(df: pd.DataFrame, athlete: str) -> pd.Series:
m = df["name"].astype(str).str.contains(athlete, case=False, na=False)
sub = df[m]
if sub.empty:
raise ValueError(f"Athlete '{athlete}' not found")
return sub.iloc[0]
rot = client.get_race(season=6, location="rotterdam", gender="male", division="open")
bcn = client.get_race(season=7, location="barcelona", gender="male", division="open")
athlete = "surname, name"
user_rot = pick_athlete_row(rot, athlete)
user_bcn = pick_athlete_row(bcn, athlete)
rot_run_avg = rot[run_cols].mean()
bcn_run_avg = bcn[run_cols].mean()
rot_sta_avg = rot[station_cols].mean()
bcn_sta_avg = bcn[station_cols].mean()
runs_cmp = pd.DataFrame({
"segment": range(1, 9),
"Rotterdam (athlete)": [user_rot[c] for c in run_cols],
"Barcelona (athlete)": [user_bcn[c] for c in run_cols],
}).set_index("segment")
stations_cmp = pd.DataFrame({
"station": station_labels,
"Rotterdam (athlete)": [user_rot.get(c, np.nan) for c in station_cols],
"Barcelona (athlete)": [user_bcn.get(c, np.nan) for c in station_cols],
}).set_index("station")
Output
Disclaimer
Pyrox is an independent project and is not affiliated with, endorsed or sponsored by the official Hyrox business and event organisers. Hyrox and related marks are trademarks of their respective owners; and they are used here only for descriptive purposes.
Client-side caching is user controlled via the use_cache flag shown above.
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 pyrox_client-0.2.3.tar.gz.
File metadata
- Download URL: pyrox_client-0.2.3.tar.gz
- Upload date:
- Size: 277.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be34280c0c2d8c552bd42239daee2d997695e1a14546b254a835e43f11a71a7c
|
|
| MD5 |
868e62ea516f50d42450bed35ba8c224
|
|
| BLAKE2b-256 |
91ecc7accc5682193f4d6ef1b5a96b39aa9eb9d79ed6db2953a5d6aef8b508ad
|
Provenance
The following attestation bundles were made for pyrox_client-0.2.3.tar.gz:
Publisher:
release.yml on vmatei2/pyrox-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrox_client-0.2.3.tar.gz -
Subject digest:
be34280c0c2d8c552bd42239daee2d997695e1a14546b254a835e43f11a71a7c - Sigstore transparency entry: 782385115
- Sigstore integration time:
-
Permalink:
vmatei2/pyrox-client@25a05c4997594c919b1bb660d1c35c3a51c641c5 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/vmatei2
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@25a05c4997594c919b1bb660d1c35c3a51c641c5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyrox_client-0.2.3-py3-none-any.whl.
File metadata
- Download URL: pyrox_client-0.2.3-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bfcd17280be0f69f90422e377e5cbfc64c80596927c3f8f52c70889f87234c1
|
|
| MD5 |
6079eb555475bc89d1ac1799ea452e41
|
|
| BLAKE2b-256 |
e1217c8845af7184c3c62ac0937e8e9c72fbd0b7e50548af80b6baf24d07e135
|
Provenance
The following attestation bundles were made for pyrox_client-0.2.3-py3-none-any.whl:
Publisher:
release.yml on vmatei2/pyrox-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrox_client-0.2.3-py3-none-any.whl -
Subject digest:
0bfcd17280be0f69f90422e377e5cbfc64c80596927c3f8f52c70889f87234c1 - Sigstore transparency entry: 782385124
- Sigstore integration time:
-
Permalink:
vmatei2/pyrox-client@25a05c4997594c919b1bb660d1c35c3a51c641c5 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/vmatei2
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@25a05c4997594c919b1bb660d1c35c3a51c641c5 -
Trigger Event:
push
-
Statement type: