API documentation
Hyrox Result API is a versioned JSON HTTP API for HYROX race data: seasons, events, results, athlete search, split times, division statistics, and simulator benchmarks.
Interactive OpenAPI explorer
Try endpoints, inspect schemas, and export the contract.
Use these docs with an LLM
Copy the full API reference as plain text (llms.txt) and paste it into ChatGPT, Claude, or Cursor to build against the API.
Quick start
- Register and subscribe , then create an API token under API tokens.
- Search for an athlete by surname and given name.
- Copy the race ID (
idfield) from a search hit. - Fetch race detail and split times with that race ID.
# 1. Search
curl -sS "https://hyroxresultapi.com/api/v1/athletes/search?last=McIntyre&first=Hunter" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
# 2. Detail (use the id field from the search response)
curl -sS "https://hyroxresultapi.com/api/v1/athletes/RACE_ID_FROM_SEARCH" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
# 3. Splits (same race ID)
curl -sS "https://hyroxresultapi.com/api/v1/athletes/RACE_ID_FROM_SEARCH/splits" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
Identifiers
The API returns opaque strings for athlete lookups. Pass them back exactly as received. Do not decode or construct them.
| Name | Where you get it | Use on |
|---|---|---|
| Race ID | id on search hits |
Detail, splits, and single race results |
| Person ref | person_ref on search hits |
Result history and splits (not detail) |
| Stored athlete ID | athlete_id on ingested result rows |
All athlete routes; optional result_id on splits |
| Event ID | id on event resources |
Division results and event statistics |
Base URL
Product endpoints live under /api/v1 on your deployment host. Examples use https://hyroxresultapi.com.
Authentication
Protected routes require a bearer token and an active subscription. Send both headers on every request:
Authorization: Bearer <your-token>
Accept: application/json
GET /api/user (no /v1 prefix) returns your plan and rate limit. It requires a token but not a subscription.
Rate limits
Limits apply per account based on your plan. Exceeding the limit returns 429 Too Many Requests with Retry-After and X-RateLimit-* headers.
- Starter: 30 requests per minute
- Pro: 100 requests per minute
- Scale: 1,000 requests per minute
Response format
{
"data": …,
"meta": { "data_source": "database" | "live", … },
"errors": null
}
Athlete routes set meta.data_source to database (catalog) or live (on demand). Errors return errors.title, errors.detail, and errors.status.
Endpoints
Paths are relative to /api/v1 unless noted.
Public
GET /api/v1/health: service and database statusGET /api/v1/openapi.yaml: OpenAPI specification
Race calendar
Upcoming races, published months ahead. One row per race — use this to let someone pick the
event they are training for. It is deliberately separate from /events,
which is one row per division per day and only appears once results are staged, roughly a week
before race day: Rome is a single entry here and seventeen there.
| Endpoint | Description |
|---|---|
| GET /races |
The HYROX race calendar: name, city code, country, continent, category and
dates, plus a ticket link. country_code
is resolved from the race venue and is null when it cannot be established yet
— it is never guessed from the city code. Upcoming by default and soonest first — pass
upcoming=false for the full listing.
Filter with from,
to,
continent
(europe,
north-america,
asia-pacific,
africa,
south-america),
category
(adults or
youngstars) and
country (ISO alpha-2) and
city_code.
A race weekend already under way still counts; races the organiser has not dated
yet are omitted.
|
| GET /events?upcoming=true | The same window seen through the results catalogue: division-level rows, ordered soonest first, for races whose result pages already exist. Useful once a race is imminent; use /races to plan ahead. |
Catalog
| Endpoint | Description |
|---|---|
| GET /seasons | List HYROX seasons. Use each slug to filter events. |
| GET /events | List events. Filter by season, country, or date range. |
| GET /events/{slug} | Single event by slug. |
| GET /events/{slug}/divisions | Divisions for an event. Division id equals the event id. |
| GET /divisions/{id}/results | Paginated finishers with cursor support. id is the event integer ID. |
Athletes
| Endpoint | Description | Accepts |
|---|---|---|
| GET /athletes/search | Search by q (surname) and first (given name). Returns race ID and person ref per hit. |
Query params |
| GET /athletes/{id} | Race detail for one result. | Race ID, stored athlete ID |
| GET /athletes/{id}/results | Result history for an athlete. | Person ref, race ID, stored athlete ID |
| GET /athletes/{id}/splits | Station split times. | Race ID, person ref, stored athlete ID |
| GET /athletes/{id}/pacing | Pro plan. One race broken down: run fade from lap 1 to lap 8, lap consistency, roxzone share, and how the finish time splits across running, stations and transitions. Includes the finish-time percentile. Needs a stored split set. | Race ID, stored athlete ID |
| GET /athletes/{id}/partners | Pro plan. Doubles and relay team-mates, with a person ref for each so you can follow one into their own history. Returns is_team_entry: false for a solo race. |
Race ID, stored athlete ID |
Stats & simulator
| Endpoint | Description |
|---|---|
| GET /percentile | Pro plan. Where one finish time ranks across the whole catalogue. Pass total_time_ms and division, optionally sex and age_group. Answers in one call what a whole finisher list would otherwise be pulled for. Cohorts widen when the exact one is too thin, and widened says so. |
| GET /stats/divisions/{id} | Precomputed event statistics, bucketed by division × gender × age group. Per-station field medians (buckets[].splits) are Scale-only; on other plans the object is present but empty and meta.field_splits.included is false. |
| GET /stats/divisions/{id}/splits | Scale plan. Per-station medians for a whole race field. Races from the last 14 days are harvested every night; an older race is backfilled on request (one race at a time, up to 5,000 finishers a day on Scale) and returns 202 until ready. Honour Retry-After; a 404 means nothing is on the way. |
| GET /simulator/benchmarks | Event-specific simulator benchmarks from stored aggregates. |
| GET /simulator/division-benchmarks | Global division benchmarks by code (e.g. HYROX_MEN). Station means across all races, not one event. |
| GET /events/{slug}/ingest-status | How far this event has progressed through ingestion — rankings and field splits — so you can stop polling blind after a race. Includes the division's own race day within a multi-day race weekend (race.race_date, race.phase), so an empty ranking that simply has not raced yet is distinguishable from a stalled ingest. |
Admin ingestion and maintenance routes under /api/v1/admin/* are documented in OpenAPI and require an admin token.
Interactive docs
Explore and try endpoints in
Swagger UI.
The OpenAPI contract file is at GET /api/v1/openapi.yaml.
CORS
For browser clients, set CORS_ALLOWED_ORIGINS in .env to a comma separated list of allowed origins.