API
The same pages,
without the HTML.
Every public page here has a JSON twin. No key, no account, no rate limit beyond ordinary good manners. Responses are cached for five minutes and send Access-Control-Allow-Origin: *, so they work from a browser on any origin.
It is read-only. There is no write API and no plan for one — everything that changes a race goes through a form with a session behind it.
GET
/runner/{username}.json
One runner, their totals, and every race they have logged.
Returns
- username, name, location, bio, favourite_quote
- avatar_url and banner_url
- links — website, instagram, strava, and up to three the runner added
- totals — races, distance_km, time_on_foot, distinct_locations, first and latest race dates
- personal_bests — one per distance, each with a link to the race
- races_per_year and races_per_city
- races — every race, with computed pace_per_km and a cover image URL
GET
/runner/{username}/{raceId}.json
One race in full, with its whole gallery.
Returns
- id, name, date, location, distance_km, finish_time, pace_per_km
- bib_number, certificate_url, notes
- strava_activity_id, where the race was imported
- photographs — every image, with caption and pixel dimensions
- runner — who it belongs to
GET
/api/explore.json
The runners who have opted into being listed.
Query parameters
- q
- Matches name or username, case-insensitively.
- city
- The runner's own stated location, matched exactly. Valid values are in the response's cities array.
- page
- 1-based. Page size is in the response.
Returns
- query — the filter that was applied, echoed back
- total, total_pages, page_size
- totals — the directory-wide figures
- cities — every value the city filter accepts
- runners — each with their latest race and both URLs
What is not in it
Dates of birth and email addresses are not reachable at any URL — the database view every public page reads through does not carry them. Strava tokens live in a table with no read policy for any public role. A runner who has left the directory keeps a public page at their own address but is absent from the directory, from the directory JSON, and from the sitemap.
If you build something on this, or if you need a field that is not here,say so. Knowing what it is used for is the only way the shape of it stays right.