games.json
/api/v1/games.json
Every completed game across every season, with final scores. One request, no pagination.
Response
Every endpoint returns the same envelope. Your data is indata — data is the array itself — there is no wrapper object inside it.
{
"schemaVersion": "1.0",
"generatedAt": "2026-06-23T14:03:13.152995-04:00",
"league": "PUL",
"data": [ ... ]
}generatedAt is a nullable ISO-8601 timestamp that may or may not carry a UTC offset — parse leniently. It is null for 2022 and 2023, which predate the pipeline that records it.
Fields
| Field | Type | Meaning |
|---|---|---|
| season | string | Season year, e.g. "2025". |
| week | number | Regular-season week number. 98 = semifinals, 99 = finals. |
| weekLabel | string|null | Human label — "Week 3", "Semifinals", "Finals". |
| isPostseason | boolean | True for semifinals and finals. Filter on this, not on the week number. |
| date | string|null | ISO date (YYYY-MM-DD). null where the date is unknown. |
| awayAbbrev | string | Canonical away-team abbreviation — join key to teams.json. |
| awayName | string | Full away-team name. |
| awayScore | number | Away goals. Final — only completed games appear here. |
| homeAbbrev | string | Canonical home-team abbreviation. |
| homeName | string | Full home-team name. |
| homeScore | number | Home goals. |
Dotted names are nested — colors.primary means{ colors: { primary } }. When the parent is a list of objects (like seasons on the manifest),seasons.season means each entry in that array has its ownseason field — access it as seasons[i].season, not seasons.season.
Examples
curl -s https://pul-stats-hub.pages.dev/api/v1/games.json | head -c 400// Every game that has ever finished 20-10
const res = await fetch('https://pul-stats-hub.pages.dev/api/v1/games.json');
const { data } = await res.json();
const matches = data.filter((g) => {
const high = Math.max(g.homeScore, g.awayScore);
const low = Math.min(g.homeScore, g.awayScore);
return high === 20 && low === 10;
});
console.log(matches.length);# How many distinct final scorelines exist? (Scorigami)
import json, urllib.request
URL = "https://pul-stats-hub.pages.dev/api/v1/games.json"
with urllib.request.urlopen(URL) as response:
games = json.load(response)["data"]
scorelines = {
(max(g["homeScore"], g["awayScore"]), min(g["homeScore"], g["awayScore"]))
for g in games
}
print(len(scorelines), "distinct scorelines in", len(games), "games")Worth knowing
- Completed games only. For unplayed or cancelled games use the per-season schedule endpoint.
- Scores come from the schedule, which is authoritative over the stats pipeline — the two can disagree and the schedule wins.
- Values change retroactively when we correct data. Check `generatedAt` if freshness matters.
