Project 06 06
Fantasy API
A local-first Python data layer that turns a volatile external feed into a dataset you can build on.
Fantasy API is a reusable Python library for fetching, organising, storing, and serving fantasy football data. It pulls player, team, match, and performance data from external sources, normalises it, keeps it on disk, and can optionally expose the stored copy through a local API.
It began as the data layer for Fantasy RL and outgrew it.
How it is put together
The library separates the two things that usually get tangled together — collecting data and using it. RequestManager handles the network, FileManager handles persistence, and a set of focused services (PlayersService, TeamsService, MarketService) sit on top of a shared BaseService to expose the domain. A single update.py entry point refreshes everything.
Alongside the live path, the repository keeps normalised static snapshots of the 2023–24 and 2024–25 seasons, so a consumer has a known-good dataset to work against without touching the network at all.
What I learned
The reason this project exists is a lesson in itself. I was training reinforcement-learning agents and could not reproduce a result from the previous week, because the data underneath had changed. An experiment that depends on a live external feed isn’t an experiment — it’s a measurement of two moving things at once. Pinning the dataset locally was what made the research above it meaningful.
Designing the manager/service split taught me how much a boundary is worth. Once every network call went through one place, rate limiting, retries, and caching became single changes rather than edits scattered across a dozen call sites. Once persistence went through another, swapping the storage format was a contained decision.
I also learned to stop treating an external API’s shape as my own. The upstream feed returns whatever it returns, with its own naming, nesting, and occasional inconsistencies; normalising at the boundary means the rest of the codebase speaks one vocabulary and never has to know what the provider changed this season.
Finally, building it as a library rather than as scripts inside the parent project was the right call for reasons I only saw later. Given a clean installable interface, the data layer became something I could point other work at — and the discipline of writing for an unknown caller made it better than it would have been as a folder of helpers.