Plus plan · Data API v1
Your club's data, on tap.
The Data API is a read-only REST API for pulling your events and participant data into spreadsheets, warehouses, or your own tools. It's included with the Plus plan.
Getting started
Authentication is a two-step flow (OAuth2 client-credentials style). Your long-lived API key is only ever used to mint short-lived access tokens; the data endpoints accept only those tokens.
- 1. Upgrade your club to Plus (Dashboard → Settings → Billing).
- 2. Open Settings → Apps and generate an API key. The full key is shown once — copy it and store it like a password.
- 3. Exchange the key for an access token (valid 15 minutes):
curl -X POST -H "Authorization: Bearer jr_YOUR_KEY" \
https://b.joinrun.club/v1/oauth/token
# → { "access_token": "eyJ…", "token_type": "Bearer", "expires_in": 900 }- 4. Call the data endpoints with the access token. Reuse it until it expires, then mint a new one.
curl -H "Authorization: Bearer eyJ…ACCESS_TOKEN" \
https://b.joinrun.club/v1/eventsBase URL: https://b.joinrun.club. All responses are JSON unless you ask for CSV.
Endpoints
/v1/oauth/tokenExchange your API key (sent as the Bearer token) for a short-lived access token. No request body is needed.
{
"access_token": "eyJhbGciOiJIUzI1NiJ9…",
"token_type": "Bearer",
"expires_in": 900
}Cache the token and reuse it for its full 15-minute lifetime — a well-behaved client mints ~4 tokens per hour. Minting is limited to 10 requests/minute per IP. Key revocation and plan changes take effect at the next mint.
/v1/eventsList your club's events, newest first, with registration and attendance counts.
- Page size, 1–500. Default 100.
limitinteger - Rows to skip. Default 0.
offsetinteger - Optional filter:
visibilitystringpublicorprivate.
{
"object": "list",
"data": [
{
"id": "9f3c…",
"slug": "sunday-long-run-9f3c12",
"title": "Sunday Long Run",
"startAt": "2026-07-12T01:00:00.000Z",
"endAt": "2026-07-12T03:30:00.000Z",
"timezone": "Asia/Kolkata",
"location": "Cubbon Park",
"visibility": "public",
"participantCount": 84,
"attendeeCount": 61,
"publicUrl": "https://joinrun.club/yourclub/sunday-long-run-9f3c12"
}
],
"limit": 100,
"offset": 0,
"total": 42,
"hasMore": false
}/v1/events/:idA single event's details, including its registration form fields — useful for interpreting the answers in the participants feed.
{
"id": "9f3c…",
"slug": "sunday-long-run-9f3c12",
"title": "Sunday Long Run",
"startAt": "2026-07-12T01:00:00.000Z",
"endAt": "2026-07-12T03:30:00.000Z",
"timezone": "Asia/Kolkata",
"location": "Cubbon Park",
"locationUrl": "https://maps.google.com/…",
"description": "Easy pace, all welcome.",
"visibility": "public",
"capacity": 120,
"fields": [
{ "id": "name", "label": "Full name", "type": "text", "required": true },
{ "id": "email", "label": "Email", "type": "email", "required": true }
],
"participantCount": 84,
"attendeeCount": 61,
"publicUrl": "https://joinrun.club/yourclub/sunday-long-run-9f3c12"
}/v1/events/:id/participantsRegistrations for one event, merged with attendance. Answers are keyed by the event's field ids.
- Page size, 1–500. Default 100.
limitinteger - Rows to skip. Default 0.
offsetinteger formatstringjson(default) orcsvfor a ready-to-open export.
{
"object": "list",
"eventId": "9f3c…",
"fields": [
{ "id": "name", "label": "Full name", "type": "text", "required": true },
{ "id": "email", "label": "Email", "type": "email", "required": true }
],
"data": [
{
"id": "77aa…",
"submittedAt": "2026-07-08T14:03:22.000Z",
"answers": { "name": "Priya Sharma", "email": "priya@example.com" },
"attended": true,
"attendedAt": "2026-07-12T01:12:41.000Z"
}
],
"limit": 100,
"offset": 0,
"total": 84,
"hasMore": false
}CSV export of the same data:
curl -H "Authorization: Bearer eyJ…ACCESS_TOKEN" \
"https://b.joinrun.club/v1/events/EVENT_ID/participants?format=csv" \
-o participants.csvPagination
List endpoints return total and hasMore. To fetch everything, keep requesting with offset += limit until hasMore is false.
Errors
Errors use conventional HTTP status codes with a JSON body: { "error": "…" }
| Status | Meaning |
|---|---|
| 401 unauthorized | Missing/revoked API key on mint, or a missing/expired access token on a data route. If a previously working token starts returning 401, it has expired — mint a new one. |
| 403 forbidden | The club's Plus subscription is no longer active. |
| 404 not_found | No such event in your club — other clubs' events always 404. |
| 429 rate_limited | Over 120 requests/minute on data routes, or over 10 mints/minute — back off and retry. |
Keep your key safe
- • Treat keys like passwords — never commit them or expose them in client-side code.
- • Only send the key to the token endpoint; use access tokens everywhere else.
- • Use one key per integration so you can revoke each independently.
- • Revoke a key anytime from Settings → Apps — it stops minting immediately, and its outstanding tokens expire within 15 minutes.
- • Keys and tokens are scoped to your club. They cannot read or write anyone else's data.
Ready to build?
Upgrade to Plus and generate your first key in minutes.