My Kids' Basketball Stats Have an API
I discovered the Dutch basketball federation runs on a public API. So naturally, I reverse-engineered it to build scouting reports for my kids' U12 teams.
It started, as most of my side projects do, with curiosity and a browser DevTools tab.
My kids Matt and Joy both play basketball for a local club. Matt and Joy each play in their own U12 team. I’m the kind of parent who sits in the stands keeping a mental tally of who scored what, so when I noticed that basketball.nl had detailed match stats on their website, I did what any developer would do: I opened the Network tab.
The API Nobody Told Me About
Turns out the Dutch basketball federation (NBB) doesn’t build their own stats platform. They use a system called FOYS, and the frontend talks to a clean REST API at api.foys.io. Public endpoints. No auth token required — just a federation ID header to identify which sport federation you’re querying.
GET https://api.foys.io/competition/public-api/v1/matches/all
x-federationid: <your-federation-id>
That’s it. Match data, player stats, team rosters, period-by-period scoring — all available with a simple HTTP request. I had a working curl command within five minutes.
Building CourtSide
What started as “let me pull some stats” quickly turned into CourtSide — a collection of scripts and HTML reports that give me deep analytics on my kids’ basketball games.
The feature list grew organically:
- Match results with period-by-period scoring breakdowns
- Season schedules with upcoming opponent analysis
- Player stats showing points per game, scoring distribution across periods
- Scouting reports on upcoming opponents — who their key players are, where they score, how they compare to our team
- Dream team lineups combining stats across the league
The reports are styled in the club’s colors — blue and gold for the club identity. Joy’s reports get a pink accent instead of gold, because she insisted, and she was right. It looks great.
U12 Mini-Basketball Is Weird (In a Good Way)
One thing I didn’t know before diving into the data: U12 mini-basketball in the Netherlands has 8 periods per match, not the standard 4 quarters. This is by design — it gives coaches more rotation opportunities and ensures all kids get court time. But it also makes the data more interesting.
With 8 periods of data per game, you can see patterns that would be invisible in a 4-quarter format. And that’s where things got fun.
What the Data Revealed
After a few weekends of collecting stats, patterns started emerging that even surprised me.
Matt is a clutch player. His scoring is heavily concentrated in the first and last periods — P1 and P8 account for 48% of his total points. He comes out strong and finishes strong. The middle periods? Quieter. Classic closer mentality.
Joy is a mid-game specialist. Her distribution is almost the inverse of Matt’s. Periods 3 and 6 represent 54% of her scoring. She takes time to read the game, finds her rhythm in the middle, and dominates when other players start to tire.
One teammate is the complete player. Across the season, she accumulated over 120 points with a distribution that covers all 8 periods. No dead zones. She scores from start to finish, which is rare at any level, let alone U12.
None of these insights are things you’d notice sitting in the stands. You’d see “Matt had a good game” or “Joy scored a lot in the second half.” The period-level data tells a different story — one about playing styles, tendencies, and matchups.
API Quirks and Gotchas
Working with an undocumented API means learning its quirks the hard way.
Period data isn’t where you’d expect it. The player stats endpoint gives you totals per game, but period-by-period breakdowns only exist in the match details endpoint. To get a player’s scoring distribution across periods, you need to fetch every match individually and join on matchPlayerId across two different response structures.
The docs don’t match the API. I found an API.md document somewhere in FOYS’s ecosystem, but the actual responses differ in meaningful ways. Match IDs are in .id not .matchId. Organization data is nested in list endpoints but flat in detail endpoints. Status values say "Planned" not "Scheduled". The status query parameter is silently ignored — you have to filter client-side. I ended up documenting all these discrepancies myself.
Privacy is inconsistent. Some players show up as “private” in the API, hiding their names. But the same players might be fully visible in match-level data. The privacy model seems to be opt-in and not consistently enforced across endpoints.
Jersey numbers are strings. Small thing, but teamNumber comes back as "23" not 23. The kind of thing that breaks a jq pipeline if you’re not careful.
The Scouting Reports
The crown jewel of CourtSide is the scouting report. Before each game, I can generate a report on the opposing team that includes:
- Their season record and points differential
- Key players ranked by points per game
- Each player’s period-by-period scoring tendency
- Players flagged as likely “playing up” from a higher team (high PPG but low game count)
- Head-to-head history if we’ve played them before
I deployed these reports using QuickShare to Firebase Hosting, so I could drop a link in the team WhatsApp group. The coaches found them genuinely useful. The parents thought I was insane. Both reactions felt correct.

The Parenting Angle
I could frame this as a pure engineering exercise — reverse-engineering an API, building data pipelines, generating reports. And it is all of those things. But honestly? The best part is having something to talk to my kids about that bridges my world and theirs.
Matt now asks me “what were my stats?” after every game. Joy wants to know how she compares to the opposing team’s best player. They’re learning to think about data and patterns, even if they’d never call it that.
There’s something satisfying about taking a thing your kids love and applying your own skills to it. Not to optimize them or turn them into athletes — just to see the game through a different lens, together.
And if that lens happens to involve curl, jq, and a public API that nobody told me about? Even better.
*Names of other players have been changed.