{season}/teams.json
/api/v1/{season}/teams.json
Team statistical totals for one season. Absent for seasons without game stats.
Seasons
Replace {season} with one of 2026, 2025, 2024, 2023, 2022. Not every season has every endpoint — check themanifest first. If it is not listed there, the URL 404s.
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 |
|---|---|---|
| abbrev | string|null | Canonical team abbreviation. |
| name | string|null | Full team name. |
| goals | number|null | Goals scored. |
| breakGoals | number|null | Goals scored while on defence. |
| blocks | number|null | Defensive blocks. |
| holds | number|null | Offensive points converted. |
| cleanHolds | number|null | Holds with no turnover. |
| passAttempts | number|null | Passes thrown. |
| turnovers | number|null | Possessions lost. |
| completedPasses | number|null | Passes completed. |
| completionRate | number|null | Completion percentage on a 0–100 scale, e.g. 89.5 — not a 0–1 fraction. |
| hucks | number|null | Long throws attempted. |
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/2025/teams.json// Most accurate offence of 2025 (completionRate is null for every 2023 team)
const res = await fetch('https://pul-stats-hub.pages.dev/api/v1/2025/teams.json');
const { data } = await res.json();
const rated = data.filter((t) => t.completionRate !== null);
const best = rated.sort((a, b) => b.completionRate - a.completionRate)[0];
console.log(`${best?.name}: ${best?.completionRate}% completion`); // 0-100 scale# Blocks leaders, 2025
import json, urllib.request
URL = "https://pul-stats-hub.pages.dev/api/v1/2025/teams.json"
with urllib.request.urlopen(URL) as response:
teams = json.load(response)["data"]
for team in sorted(teams, key=lambda t: t["blocks"], reverse=True)[:5]:
print(team["abbrev"], team["blocks"])Worth knowing
- Absent for 2022 — that season has no game-level stats, so the endpoint 404s rather than returning zeros. Check the manifest before requesting it.
- Totals cover the regular season. Postseason stats are tracked separately and are not folded in here.
- 2023 predates full Statto exports: `breakGoals`, `holds`, `cleanHolds`, `passAttempts`, `completedPasses`, `completionRate`, and `hucks` are null on every 2023 row. `goals`, `blocks`, and `turnovers` are populated for 2023.
