Introduction
Read-only, server-to-server access to upcoming Southern African running events.
The RunningCalendar API is a versioned, read-only HTTP API for trusted third-party applications. It exposes upcoming, publicly-visible running events so you can show calendar data and link users back to runningcalendar.co.za.
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer rc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Send your key as a bearer token: Authorization: Bearer rclive.... Keys are issued by the RunningCalendar team and are secret — keep them server-side. A disabled or revoked key returns 403; an unknown or missing key returns 401.
Events
List upcoming events
requires authentication
Returns a paginated list of upcoming, publicly-visible event editions,
ordered by date ascending. Mirrors the public website's visibility
exactly. Every item carries links to its page on the website: url for
your website and app_url for your app, both tagged so the visits are
credited to your key, and an untagged canonical_url for matching.
When updated_since is provided the endpoint switches to sync mode:
it returns every edition whose updated_at >= updated_since, regardless
of visibility, so a consumer can replay all changes since its last poll.
Each item carries a sync_status field — "active" means the edition
is currently live on the calendar; "removed" means it was changed but
is no longer publicly visible (draft, cancelled, or past). A removed
item carries only id, slug, sync_status, and updated_at — never
full event data — to avoid leaking non-public content. Browse filters
(country, province, city, distance) are ignored in sync mode;
only updated_since and pagination apply.
Known sync limitations:
- Hard deletes are not reported.
EventEditionhas no soft-deletes, so a hard-deleted row never surfaces asremoved. Reliable delete-sync would require addingSoftDeletestoEventEdition— out of scope. - Time-based expiry is not a change event. An edition that simply became
past does not get a new
updated_at, so it will not surface asremovedfrom the passage of time alone. Expire past events locally using thedatefield you already hold. - Boundary de-dup. The filter is inclusive (
>=). Checkpoint on the maxupdated_atseen and re-request with it, de-duplicating byid(a handful of repeats at the timestamp boundary is expected and harmless).
Example request:
curl --request GET \
--get "https://api.runningcalendar.co.za/v1/events?from=2026-07-01&to=2026-12-31&country=ZA&province=gauteng&city=cape-town&distance=10km%2Chalf-marathon&updated_since=2026-06-01T00%3A00%3A00Z&per_page=25&page=1" \
--header "Authorization: Bearer rc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-10-22T07:13:49\",
\"to\": \"2052-11-14\",
\"updated_since\": \"2026-10-22T07:13:49\",
\"country\": \"ng\",
\"province\": \"z\",
\"city\": \"m\",
\"distance\": \"architecto\",
\"per_page\": 22
}"
const url = new URL(
"https://api.runningcalendar.co.za/v1/events"
);
const params = {
"from": "2026-07-01",
"to": "2026-12-31",
"country": "ZA",
"province": "gauteng",
"city": "cape-town",
"distance": "10km,half-marathon",
"updated_since": "2026-06-01T00:00:00Z",
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer rc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-10-22T07:13:49",
"to": "2052-11-14",
"updated_since": "2026-10-22T07:13:49",
"country": "ng",
"province": "z",
"city": "m",
"distance": "architecto",
"per_page": 22
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Success):
{
"data": [
{
"id": 1,
"name": "Cape Town Marathon",
"slug": "cape-town-marathon",
"date": "2026-10-18",
"end_date": null,
"summary": "The 2026 Cape Town Marathon takes place on Sunday, 18 October 2026, at Green Point, Cape Town. It is a single 42.2km road race. Online entries are open.",
"organiser": {
"name": "Cape Town Marathon NPC",
"url": "https://runningcalendar.co.za/organisers/cape-town-marathon-npc?utm_source=example&utm_medium=referral&utm_campaign=events_api",
"app_url": "https://runningcalendar.co.za/organisers/cape-town-marathon-npc?utm_source=example&utm_medium=app&utm_campaign=events_api"
},
"races": [
{
"name": "42.2km",
"distance_metres": 42195,
"distance_label": "Marathon",
"race_type": "road",
"start_time": "06:00",
"race_fee_from": 250,
"race_fee_currency": "ZAR"
}
],
"venue": {
"town": "Cape Town",
"province": "Western Cape",
"country_iso2": "ZA",
"lat": -33.9249,
"lng": 18.4241
},
"image_url": "https://media.runningcalendar.co.za/7441/cape-town-marathon-logo.png",
"verification_status": "verified",
"url": "https://runningcalendar.co.za/events/cape-town-marathon/2026?utm_source=example&utm_medium=referral&utm_campaign=events_api",
"app_url": "https://runningcalendar.co.za/events/cape-town-marathon/2026?utm_source=example&utm_medium=app&utm_campaign=events_api",
"canonical_url": "https://runningcalendar.co.za/events/cape-town-marathon/2026"
}
],
"links": {
"first": "...",
"last": "...",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1,
"source": "RunningCalendar",
"docs_url": "https://api.runningcalendar.co.za/docs"
}
}
Example response (200, Sync mode (updated_since)):
{
"data": [
{
"id": 1,
"name": "Cape Town Marathon",
"slug": "cape-town-marathon",
"date": "2026-10-18",
"end_date": null,
"summary": "The 2026 Cape Town Marathon takes place on Sunday, 18 October 2026, at Green Point, Cape Town. It is a single 42.2km road race. Online entries are open.",
"organiser": {
"name": "Cape Town Marathon NPC",
"url": "https://runningcalendar.co.za/organisers/cape-town-marathon-npc?utm_source=example&utm_medium=referral&utm_campaign=events_api",
"app_url": "https://runningcalendar.co.za/organisers/cape-town-marathon-npc?utm_source=example&utm_medium=app&utm_campaign=events_api"
},
"races": [
{
"name": "42.2km",
"distance_metres": 42195,
"distance_label": "Marathon",
"race_type": "road",
"start_time": "06:00",
"race_fee_from": null,
"race_fee_currency": null
}
],
"venue": {
"town": "Cape Town",
"province": "Western Cape",
"country_iso2": "ZA",
"lat": -33.9249,
"lng": 18.4241
},
"image_url": "https://media.runningcalendar.co.za/7441/cape-town-marathon-logo.png",
"verification_status": "verified",
"url": "https://runningcalendar.co.za/events/cape-town-marathon/2026?utm_source=example&utm_medium=referral&utm_campaign=events_api",
"app_url": "https://runningcalendar.co.za/events/cape-town-marathon/2026?utm_source=example&utm_medium=app&utm_campaign=events_api",
"canonical_url": "https://runningcalendar.co.za/events/cape-town-marathon/2026",
"sync_status": "active",
"updated_at": "2026-06-01T08:00:00Z"
},
{
"id": 2,
"slug": "old-cancelled-race",
"sync_status": "removed",
"updated_at": "2026-06-01T09:00:00Z"
}
],
"links": {
"first": "...",
"last": "...",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"per_page": 25,
"total": 2,
"source": "RunningCalendar",
"docs_url": "https://api.runningcalendar.co.za/docs"
}
}
Example response (401, Missing or invalid key):
{
"error": {
"code": "unauthenticated",
"message": "Invalid API key."
}
}
Example response (429, Rate limited):
{
"error": {
"code": "rate_limited",
"message": "Too many requests."
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Response
Response Fields
data
object
summary
string|null
A short, generated plain-text summary of the event (where, which distances, entry status). null when there is nothing to say.
organiser
object|null
Who organises the event, or null when unknown. url / app_url link to the organiser's page on RunningCalendar (tagged like the event links) and are null when there is no public page.
image_url
string|null
The event's logo, sized for listings (at most 600×600), or null when the event has no logo. Load it from this URL: don't copy it, so updates reach your site.
url
string
Link to this event's page for use on your website. It carries tracking parameters that identify your key: use it exactly as returned, and don't add, change or strip parameters.
app_url
string
The same page, tagged for links opened from a mobile app. Use it instead of url inside an app.
canonical_url
string
The event page with no parameters. It never changes for an edition: use it (or id) to match and deduplicate events, not as a link.
races
object
race_fee_from
number|null
The senior entry fee ("Entry Fee") for the race. 0 means free entry; null means no senior fee is listed. Junior, early-bird, 60+ and licence fees are never used.
race_fee_currency
string|null
ISO 4217 currency code of race_fee_from, e.g. ZAR. null whenever race_fee_from is null.
Fetch a single event
requires authentication
Looks up one event edition by numeric id or by event slug. Applies the exact same visibility scope as the list — a draft, unpublished, hidden, or past edition returns 404 (never a leak). The payload uses the same basic fields as a list item.
Example request:
curl --request GET \
--get "https://api.runningcalendar.co.za/v1/events/cape-town-marathon" \
--header "Authorization: Bearer rc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://api.runningcalendar.co.za/v1/events/cape-town-marathon"
);
const headers = {
"Authorization": "Bearer rc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Found):
{
"data": {
"id": 1,
"name": "Cape Town Marathon",
"slug": "cape-town-marathon",
"date": "2026-10-18",
"end_date": null,
"summary": "The 2026 Cape Town Marathon takes place on Sunday, 18 October 2026, at Green Point, Cape Town. It is a single 42.2km road race. Online entries are open.",
"organiser": {
"name": "Cape Town Marathon NPC",
"url": "https://runningcalendar.co.za/organisers/cape-town-marathon-npc?utm_source=example&utm_medium=referral&utm_campaign=events_api",
"app_url": "https://runningcalendar.co.za/organisers/cape-town-marathon-npc?utm_source=example&utm_medium=app&utm_campaign=events_api"
},
"races": [
{
"name": "42.2km",
"distance_metres": 42195,
"distance_label": "Marathon",
"race_type": "road",
"start_time": "06:00",
"race_fee_from": 250,
"race_fee_currency": "ZAR"
}
],
"venue": {
"town": "Cape Town",
"province": "Western Cape",
"country_iso2": "ZA",
"lat": -33.9249,
"lng": 18.4241
},
"image_url": "https://media.runningcalendar.co.za/7441/cape-town-marathon-logo.png",
"verification_status": "verified",
"url": "https://runningcalendar.co.za/events/cape-town-marathon/2026?utm_source=example&utm_medium=referral&utm_campaign=events_api",
"app_url": "https://runningcalendar.co.za/events/cape-town-marathon/2026?utm_source=example&utm_medium=app&utm_campaign=events_api",
"canonical_url": "https://runningcalendar.co.za/events/cape-town-marathon/2026"
}
}
Example response (404, Not found / not visible):
{
"error": {
"code": "not_found",
"message": "Resource not found."
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Response
Response Fields
data
object
summary
string|null
A short, generated plain-text summary of the event (where, which distances, entry status). null when there is nothing to say.
organiser
object|null
Who organises the event, or null when unknown. url / app_url link to the organiser's page on RunningCalendar (tagged like the event links) and are null when there is no public page.
image_url
string|null
The event's logo, sized for listings (at most 600×600), or null when the event has no logo. Load it from this URL: don't copy it, so updates reach your site.
url
string
Link to this event's page for use on your website. It carries tracking parameters that identify your key: use it exactly as returned, and don't add, change or strip parameters.
app_url
string
The same page, tagged for links opened from a mobile app. Use it instead of url inside an app.
canonical_url
string
The event page with no parameters. It never changes for an edition: use it (or id) to match and deduplicate events, not as a link.
races
object
race_fee_from
number|null
The senior entry fee ("Entry Fee") for the race. 0 means free entry; null means no senior fee is listed. Junior, early-bird, 60+ and licence fees are never used.
race_fee_currency
string|null
ISO 4217 currency code of race_fee_from, e.g. ZAR. null whenever race_fee_from is null.