Race API
Yarış Radarı scans ten sources every day and gathers road and trail running, cycling, swimming, triathlon and HYROX races in Türkiye into a single calendar. This read-only JSON API opens the same dataset to applications: dates, cities and coordinates, sports, courses (distance, elevation gain, ITRA points, fee), registration windows and deadlines, official site and results links. An optional per-race highlights object adds the course GPX and map image, checkpoints, results archive and participant counts to the same response.
Built for apps, training platforms and publications that need an up-to-date race calendar. Base URL: https://yarisradari.com/api/v1
Authentication
Send your key with either header on every request. Keys are meant for server-to-server use.
Authorization: Bearer yr_live_xxxxxxxx X-API-Key: yr_live_xxxxxxxx
GET/races
A filterable race list. All parameters are optional; csv types accept several comma-separated values. When distance_min/max is given, the returned distances array is filtered down to the matching courses.
| Parameter | Type | Description |
|---|---|---|
| scope | csv | local, national, international |
| sport | csv | road_run, trail_run, cycling, swimming, triathlon, hyrox, duathlon, orienteering, obstacle_run |
| status | csv | scheduled, tbc, postponed, cancelled, completed |
| city | text | Exact city match, case insensitive |
| q | text | Partial search in the race name |
| from | YYYY-MM-DD | Start date on or after this date |
| to | YYYY-MM-DD | Start date on or before this date |
| updated_since | ISO date/time | Only records updated after this moment; for incremental sync |
| distance_min | number (km) | Races with a course at least this long |
| distance_max | number (km) | Races with a course at most this long |
| sort | text | start_date (default), -start_date, updated_at, -updated_at |
| limit | integer | 1-100, default 25 |
| offset | integer | 0-10000, pagination |
curl "https://yarisradari.com/api/v1/races?sport=trail_run&from=2026-09-01&limit=5" \ -H "Authorization: Bearer yr_live_xxxxxxxx"
Response:
{
"data": [
{
"id": "uuid",
"slug": "istanbul-maratonu-2026",
"name": "İstanbul Maratonu",
"sport": "road_run",
"scope": "national",
"status": "scheduled",
"city": "İstanbul",
"venue": null,
"country_code": "TR",
"start_date": "2026-11-01",
"end_date": null,
"latitude": 41.0,
"longitude": 28.9,
"website_url": "https://...",
"results_url": null,
"photos_url": null,
"description_tr": "...",
"description_en": null,
"distances": [
{
"label": "Maraton",
"distance_km": 42.195,
"elevation_gain_m": null,
"itra_points": null,
"price": 1500,
"currency": "TRY",
"gpx_url": null
}
],
"registration": [
{
"kind": "standard",
"opens_at": null,
"closes_at": "2026-10-20T23:59:00+03:00",
"price": null,
"currency": "TRY",
"is_lottery": false,
"registration_url": "https://..."
}
],
"highlights": {
"route": {
"courses_with_gpx": 2,
"gpx_url": "https://...",
"map_image_url": "https://yarisradari.com/yaris/istanbul-maratonu-2026/parkur/maraton-parkur-profili.svg"
},
"briefing": true,
"checkpoints": { "count": 8, "with_cutoff": 6, "courses": 2 },
"results": { "editions": 4, "last_edition_year": 2025, "courses": 3 },
"participants": { "last_edition_year": 2025, "starters": 2972, "finishers": 2954 }
},
"created_at": "...",
"updated_at": "..."
}
],
"meta": { "total": 90, "count": 25, "limit": 25, "offset": 0, "request_id": "uuid" }
}highlights: extra race facts
Every race object may carry an optional highlights object. The rule: a key is sent only when that race really has the data, never as an empty key, null or false, so the presence of a key means the data exists. Values are structural, not ready-made sentences; the consumer builds its own wording. New keys may be added and existing keys are never renamed, so you can ignore a key you do not know. Weather stays out of this object because forecasts go stale fast.
| Key | Fields | When it appears |
|---|---|---|
route | courses_with_gpx, gpx_url, map_image_url | When at least one course has a GPX file. gpx_url is the organiser file, map_image_url is our own route and elevation profile image (SVG); it returns 404 if the source file cannot be read. |
briefing | true | When at least one course has a technical meeting built from its GPX elevation data. Only true is sent; without an analysis the key is absent. The full text lives on the race page. |
checkpoints | count, with_cutoff, courses | When the race checkpoints are known. with_cutoff counts the ones with a cut-off time. |
results | editions, last_edition_year, courses | When past seasons are in the aggregate results archive. No runner names or placings are stored. |
participants | last_edition_year, starters, finishers | When the starter and finisher counts of the last season are known. The numbers are the sum of every course that season; a number that is unknown is left out. |
GET/races/{slug}
A single race, with the same field structure as the list: { "data": { ... }, "meta": { "request_id": "uuid" } }. Returns 404 if not found.
GET/tools/hazirlik-suresi
The open, keyless endpoint of the training lead time calculator: pass a race slug or a raw distance and it returns the number of weeks needed, the peak week and the latest day to start. Keys, quota and the per-minute limit do not apply here; responses are cached for 5 minutes.
| Parameter | Type | Description |
|---|---|---|
| yaris | string | Race slug. Without a course, the longest course of the race is used. |
| parkur | string | Course label (e.g. 63K) or the <raceId>:<label> form. |
| km | number | Distance in km when no race is given. |
| tirmanis | number | Elevation gain in metres, default 0. |
| haftalik | number | Your current weekly running distance in km, default 30. The result depends on it. |
curl "https://yarisradari.com/tools/hazirlik-suresi?yaris=alanya-ultra-trail&haftalik=30"
Response:
{
"input": {
"km": 90,
"elevation_gain_m": 4500,
"weekly_km": 30,
"run_days": 4,
"today": "2026-09-18",
"race": {
"name": "ALANYA ULTRA TRAIL",
"slug": "alanya-ultra-trail",
"course": "90K",
"start_date": "2027-03-27"
}
},
"result": {
"weeks": 23,
"months": 5.5,
"peak_weekly_km": 95,
"peak_long_run_km": 35,
"start_by": "2026-10-17",
"weeks_left": 27,
"late": false,
"volume_ready": false
},
"sentence": {
"tr": "ALANYA ULTRA TRAIL 90K parkuru için haftada 30 km koşan biri hazırlığa 23 hafta, yaklaşık 5,5 ay ayırmalı; en geç 17 Ekim 2026 Cumartesi günü başlamalı. Zirve hafta 95 km efor, en uzun antrenman koşusu 35 km.",
"en": "Someone running 30 km a week needs 23 weeks, about 5.5 months, to prepare for the 90K course of ALANYA ULTRA TRAIL; the latest start is Sat, Oct 17, 2026. The peak week reaches 95 effort km with a 35 km long run."
},
"page": "https://yarisradari.com/araclar/hazirlik-suresi?yaris=alanya-ultra-trail&parkur=90K&haftalik=30"
}The sentence field carries a ready-made sentence in both Turkish and English, so it can be handed straight to an agent. The page field points to the human-readable version of the same calculation.
JavaScript example
const res = await fetch(
'https://yarisradari.com/api/v1/races?sport=trail_run&distance_min=40',
{ headers: { Authorization: `Bearer ${process.env.YARISRADARI_API_KEY}` } },
)
const { data, meta } = await res.json()Limits and errors
The monthly quota and the per-minute limit are per key; track what is left through the X-Quota-Remaining and X-RateLimit-Remaining response headers. Every response carries X-Request-Id for log correlation. Errors come back in this format: { "error": "kod", "request_id": "uuid" }
| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_scope, invalid_sport, … | Invalid parameter |
| 401 | missing_api_key / invalid_api_key | Key missing or wrong |
| 404 | not_found | Race not found |
| 429 | quota_exceeded | Monthly quota used up |
| 429 | rate_limit_exceeded | Per-minute limit exceeded |
| 503 | service_unavailable | Temporary server problem, see the Retry-After header |
Licence and terms of use
Version 2026-08-05. Receiving a key counts as accepting these terms.
Allowed
- Showing the data in your own app, site or bot
- Building race lists, calendars, alerts and search features
- Caching the data in your own database and keeping it in incremental sync
Required
- A "Source: yarisradari.com" note and a link to the site wherever the data is shown
- Keeping the key secret and using it server-side
Not allowed
- Redistributing the data as another API or selling it on
- Sharing the key or opening your quota to third parties
- Using the Yarış Radarı name in a way that implies endorsement or partnership
The data is compiled automatically from organiser and federation sources, carries a margin of error and comes with no warranty. Before entering a race, the organiser official page prevails.
Machine-readable definition
All endpoints, parameters and the response schema are published as OpenAPI 3.1 at /openapi.json . Use that file to generate a client or to plug the API into an agent.
Access key
The API works with an access key. If you want to use it, write through the contact page and we will work out a plan that fits your needs.
Frequently asked questions
(4)
Is there a single data source for races in Türkiye?
Yarış Radarı compiles road and trail running, cycling, swimming, triathlon and HYROX races in Türkiye every day from federation calendars, timing platforms and organiser websites, and the major world races are in the calendar too. This API opens that same dataset as JSON.
What does the Race API return?
Dates, city and coordinates, sport, courses (distance, elevation gain, ITRA points, fee, GPX link), registration windows and deadlines, official site, entry and results links, and race descriptions. Races that have the data also carry a highlights object: course GPX and map image, checkpoint counts, the seasons in the results archive and the starter and finisher counts of the last season.
How often is the data updated?
The calendar is refreshed by an automatic scan every morning; with the updated_since parameter you can pull only changed records and run an incremental sync.
Is the Race API free, and how do I get access?
Access works with a key: write through the contact page and we will agree on a plan that fits your usage.