API documentation — v1
Everything the API does, in one page. A machine-readable OpenAPI document is also available.
Base URL and authentication
https://rugbyclubhouse.co.uk/api/v1
Every endpoint except /health requires your API key in a header:
X-Api-Key: rck_your_key_here
Keys are issued with your account and shown in full exactly once. A missing,
invalid or revoked key returns 401. If your key is ever exposed,
contact us and we'll revoke and reissue it immediately.
Conventions
- All endpoints are HTTPS
GETand return JSON with camelCase properties. - Dates are
yyyy-MM-ddstrings.meta.generatedAtis server time with the UK offset;dataUpdatedAtandupdatedAtare database timestamps, offset-free. - No pagination: a league season is small enough to return whole, and we document that so you don't build for paging that never comes.
- Changes within v1 are additive only. Anything breaking becomes v2 at a new route prefix.
- Responses are cached server-side for up to five minutes. There is no hard rate limit at present; usage is metered per key and fair use applies.
The envelope
Every collection response wraps its data in an envelope. meta carries
the generation time, the freshness of the returned rows, and the resolved scope:
{
"meta": {
"generatedAt": "2026-07-17T10:46:25.12+01:00",
"dataUpdatedAt": "2026-07-15T17:41:35.043",
"season": { "seasonId": 1, "seasonLabel": "2025-2026" },
"competition": { "competitionId": 1, "competitionName": "Counties 2 Sussex" }
},
"data": [ ... ]
}
Wherever an endpoint accepts seasonId it is optional; omit it and you
get the latest season with data for that scope. The resolved season is always
echoed in meta.season, so you always know what you were given.
Errors
One shape everywhere:
{ "error": { "code": "invalid_key", "message": "The API key is missing, invalid, or revoked." } }
| HTTP | code | When |
|---|---|---|
| 400 | invalid_parameter | A query parameter that should be an integer isn't |
| 401 | invalid_key | Missing, unknown or revoked key |
| 404 | not_found | Unknown competition, team or route |
| 500 | server_error | Our fault; logged on our side, no detail leaked |
Endpoints
GET /health no key needed
The honest heartbeat: entity counts and the most recent data change.
{
"status": "ok",
"seasons": 1, "competitions": 1, "teams": 10,
"fixtures": 89, "standings": 10,
"dataUpdatedAt": "2026-07-15T17:41:35.043"
}
GET /seasons
Seasons that hold data, newest first.
{ "seasonId": 1, "seasonLabel": "2025-2026" }
GET /competitions?seasonId=
Competitions with data in the resolved season.
{ "competitionId": 1, "competitionName": "Counties 2 Sussex" }
GET /competitions/{competitionId}/teams?seasonId=
Teams appearing in that competition and season, alphabetical.
{ "teamId": 7, "teamName": "Pulborough" }
GET /competitions/{competitionId}/fixtures?seasonId=&teamId=
All fixtures and results for the competition, date order. Add teamId
to filter to one team. A null score.home means the fixture is
unplayed; a populated score means it's a result. fixtureDate can move
between calls (postponements). kickoff, status,
venue, attendance, referee and the
half-time scores are supplied only when the source provides them.
{
"fixtureId": 1,
"season": { "seasonId": 1, "seasonLabel": "2025-2026" },
"competition": { "competitionId": 1, "competitionName": "Counties 2 Sussex" },
"roundLabel": null,
"fixtureDate": "2025-10-04",
"kickoff": null,
"homeTeam": { "teamId": 9, "teamName": "Seaford" },
"awayTeam": { "teamId": 4, "teamName": "East Grinstead" },
"score": { "home": 27, "away": 10, "homeHalfTime": null, "awayHalfTime": null },
"status": null,
"venue": null,
"attendance": null,
"referee": null,
"matchUrl": "https://www.englandrugby.com/...",
"updatedAt": "2026-07-15T17:41:35.02"
}
GET /teams/{teamId}/fixtures?seasonId=
One team's fixtures and results across every competition we cover, in the resolved season. Same item shape as above; each item self-describes its competition.
GET /competitions/{competitionId}/standings?seasonId=
The full league table in position order. bonusPoints is the combined
figure to display; tryBonus and losingBonus are its
breakdown. points is the league total after any deduction.
form and the home/away record splits are enrichment fields: null
unless the source supplies them, and never required.
{
"position": 1,
"team": { "teamId": 7, "teamName": "Pulborough" },
"played": 18, "won": 16, "drawn": 1, "lost": 1,
"pointsFor": 737, "pointsAgainst": 182, "pointsDiff": 555,
"tryBonus": 14, "losingBonus": 1, "bonusPoints": 15,
"pointsDeducted": null,
"points": 82,
"form": null,
"homeRecord": { "wins": null, "draws": null, "losses": null, "pointsFor": null },
"awayRecord": { "wins": null, "draws": null, "losses": null, "pointsFor": null },
"updatedAt": "2026-07-15T17:41:35.043"
}
Worked examples
GET /api/v1/competitions the leagues we cover, latest season
GET /api/v1/competitions/1/standings the current table
GET /api/v1/competitions/1/fixtures?teamId=7 one team's league season
GET /api/v1/teams/7/fixtures one team across all competitions
GET /api/v1/health is everything up, and how fresh
Questions, coverage requests, or a key of your own: get in touch.