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 GET and return JSON with camelCase properties.
  • Dates are yyyy-MM-dd strings. meta.generatedAt is server time with the UK offset; dataUpdatedAt and updatedAt are 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." } }
HTTPcodeWhen
400invalid_parameterA query parameter that should be an integer isn't
401invalid_keyMissing, unknown or revoked key
404not_foundUnknown competition, team or route
500server_errorOur 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.