Skip to content
HomeAboutProjectsContact

Navigation

HomeAboutProjectsContactResume

Connect

All projects
FootballSeer

FootballSeer

FootballSeer prices around 285 football matches a day across 38 leagues, plus basketball across seven (NBA, WNBA, NCAA, G League, NBL, EuroLeague and EuroCup). A Python pipeline refits the models every day and exports JSON; a Next.js front end turns it into 480+ static pages: a match board for every market, value bets, a screener, a bet generator, a matchup predictor, written previews, live scores and a public track record that compares the model with the bookmakers. It runs end to end on GitHub Actions and Vercel for about $0 a month.

Context

Personal project ยท live product

Role

Creator & Engineer

Year

2026

Stack

PythonNumPySciPypandasNext.jsTypeScriptTailwind CSSGitHub ActionsVercel
Visit site

The problem

Most prediction sites sell tips without ever showing how they did. I wanted one model that prices every market consistently, publishes each prediction before kick-off, and keeps a record anyone can check.

What I built

  • Built the Python pipeline: results and bookmaker odds from football-data.co.uk (with a Wayback Machine fallback), one Dixon-Coles model per league refitted daily, and every market derived from a single scoreline probability table
  • Added a basketball model, a time-weighted ridge regression on points with per-league decay and shrinkage, pricing moneyline, spread, totals, halves and quarters from ESPN and EuroLeague feeds
  • Wrote a walk-forward backtest and a prediction ledger committed to git before kick-off, so the track record is auditable
  • Built the Next.js front end: 480+ statically generated pages, filters, value bets, bet generator, matchup predictor, previews and live scores
  • Automated the whole loop: GitHub Actions refreshes the data twice a day, commits it, and the push redeploys on Vercel
  • Designed the brand mark and a guided first-visit tour that walks new users through the product

Key decisions

One scoreline table behind every market

Every market (1X2, goals lines, both teams to score, correct score) is read off the same matrix of scoreline probabilities, so prices can never contradict each other. A new market is a few lines of code, not a new model.

Static by default, serverless only for live data

Every page is prerendered from committed JSON and served from the CDN. Live scores go through a small serverless proxy with 15โ€“20 second caching, so however many people are watching, the upstream API sees a few requests a minute. Hosting costs about nothing.

Publish the record, including the bad parts

The track record sets the model against bookmaker prices. The model is well calibrated, but it doesn't beat the bookmakers yet, and the site says so rather than hiding it. That honesty is the product.

Tracking down a build that never finished

After adding basketball, production builds froze partway through static generation. I bisected the 481 pages down to a single game page, then to one sentence of its preview, and traced it to Next.js's experimental partial prefetching. Turning that off fixed the build without changing what users see.

Outcome

Backtested on 14,205 football matches (August 2025 to October 2026) and 9,567 basketball games. When the model rates a home win at 70% or more, it happens 81% of the time across 430 matches; basketball favourites rated 70% or more win 83% of 3,575 games. Against the market it still trails (log loss 1.026 against the bookmakers' 1.006, and backtested value bets lose 13%), so the next step is richer inputs: expected goals, lineups and injuries.

โ† Apperio SDKCerebro โ†’

Let's work
together.

Send an emailBook a call

Navigation

HomeAboutProjectsContact

Connect

TwitterGitHubInstagramLinkedIn

Site

Built with Next.js, Tailwind CSS, and GSAP.

Deployed on Vercel.

ยฉ 2026 Femi Ajanaku. All rights reserved.

Relentless by design.