Free forecasts for about 95 sports leagues, from models that publish their own track record.
Objective
Make honest sports forecasts free and legible: a win probability for every game or race, season and title odds, and a public record of how the models have done on seasons they never saw.
The home page: every league with today's games, each with the model's win probability.
What it does
Win probabilities for upcoming games and races across about 95 leagues, from SailGP and Formula 1 to the NFL, the Premier League, cricket, tennis and college sports.
Season simulations: title odds, playoff chances and projected standings, rebuilt twice a day and whenever a result lands.
Team and player ratings, a stats explorer and CSV downloads for every league.
A track record page per league, scored on holdout seasons the model was never tuned on.
Bookmaker and prediction-market prices next to the model's, collected every three hours.
An assistant that answers questions from the site's own data, a discussion board per league, and a live chat on every game.
Accounts, a paid plan, and ads only for visitors without one.
Design choices, and why
Freeze the parameters, publish the score
A model's settings are fitted once on tuning seasons and saved in version control. Every ordinary rebuild re-scores the holdout seasons with those frozen settings, so the track record cannot quietly improve by refitting on the seasons it is graded on. Refitting is a deliberate, committed act.
One kit, one plugin per league
Each league is a small plugin on a shared kit of ratings, match and race models, simulations and backtests. A new league appears on the site the first time it is built, with no change to the site code, which is how the count reached 95.
Static data at the edge, logic in a Worker
Each league builds to JSON files served from Cloudflare's edge, so pages are fast everywhere and a traffic spike costs nothing. A Worker renders a snapshot of every page for search engines and answers the API: chat, live scores, accounts and the community.
Show the market next to the model
Bookmaker and prediction-market prices sit beside the forecast on every game. It keeps the model honest in public and gives readers a second opinion without leaving the page.
Challenges and how we solved them
Scheduled jobs that ran hours late
GitHub's own cron can fire long after its time, which is useless during a live round. A tiny scheduler Worker checks every five minutes and starts the runs itself, including live rebuilds while games are on and a daily freshness watchdog that reports any league whose page is older than it should be.
Rate limits on every data source
Weather lookups, odds feeds and league APIs all cap requests. The pipeline caches everything it has already asked for, carries on without a missing piece and fills it in on the next run, and spends counted requests only where they change a page.
Moderating a community without a moderator
Anyone can read the discussions and chats; accounts can post. Every post passes a word filter and a cheap AI check before it appears, with rate limits per visitor and per address, so the boards stay usable without someone watching them all day.
Live chat that must survive a deploy
The chat rooms hold open connections, which a normal deploy would drop. They live in Durable Objects in a Worker of their own, deployed separately from the site, so a site release never disconnects a room.
Where it stands
PodiumOracle is live at podiumoracle.com with about 95 leagues, rebuilt twice a day on self-hosted runners, with odds collected every three hours and monitoring through Sentry, Slack and PostHog. It is the product behind our data-pipeline and forecasting work for clients.