Skip to main content
YK logo
← Back to projects

AccaIQ — AI-Powered Football Accumulator Predictor

A Next.js app that uses Claude to reason about form, H2H history, and bookmaker odds to surface value in football accumulator bets

Overview

AccaIQ is a Next.js app that helps build football accumulator bets by combining live fixture data across the Premier League, La Liga, and Bundesliga with an AI reasoning layer powered by Claude. It analyses team form, head-to-head history, and injury data to suggest statistically sound acca combinations, and compares Claude's estimated win probabilities against bookmaker-implied odds to surface legs where the market might be mispricing an outcome.


The problem

Building an accumulator by hand means juggling form guides, injury news, and odds across several fixtures and leagues at once, then doing the implied-probability maths yourself to work out whether a price is actually good value or just looks tempting. The interesting engineering problem wasn't fetching fixture data — that's a straightforward API integration — it was building a reasoning pipeline that takes noisy, qualitative signal (recent form, head-to-head record, who's injured) and turns it into a probability estimate that can be meaningfully compared against a bookmaker's implied odds.


What I built

A reasoning pipeline over fixture and odds data

/api/suggest is a POST endpoint that runs fixtures and context through Claude and gets back structured confidence scores per leg, handled in lib/claude.ts. The interesting part is upstream of the AI call: lib/api-football.ts and lib/odds-api.ts pull in fixture and market-odds data respectively, and the prompt has to package form, H2H, and injury context in a way that produces a usable, structured probability estimate rather than a vague paragraph of football punditry.

Value identification against bookmaker odds

ValueBadge.tsx and the probability maths in lib/utils.ts convert bookmaker odds to implied probability and compare that against Claude's estimate. Where the model's estimated win probability is meaningfully higher than what the odds imply, that's flagged as value. This is the actual point of the product — not "predict the outcome," which is a much harder and less useful problem, but "is this price good relative to a reasonable probability estimate," which is closer to how value betting actually works.

Demo mode with no API keys required

The app runs with mock fixture data (lib/mock-data.ts) when API_FOOTBALL_KEY, ODDS_API_KEY, and ANTHROPIC_API_KEY aren't set, rather than failing to start or showing an empty screen. This matters more than it might seem — it means the app is actually inspectable by someone cloning the repo without them first having to go and sign up for three separate API accounts, and it kept my own local development moving during phases where I was iterating on UI rather than the AI pipeline.

Caching to respect rate limits

A 15-minute in-memory TTL cache (lib/cache.ts) sits in front of the fixture API, which matters practically — the free tier of API-Football is capped at 100 requests a day, and fixture data for a given date doesn't change minute to minute, so caching isn't an optimisation here, it's the difference between the app working and hitting a rate limit by lunchtime.

The fixture browser UI

A date-strip navigator (DateStrip.tsx), league filter toggles, fixture cards showing form pills and head-to-head summaries (FixtureCard.tsx, FormPills.tsx), and a slide-up acca-builder panel (AccaSlip.tsx) backed by a dedicated useAccaSlip hook for managing the in-progress bet slip state, all on Tailwind CSS with dark mode as the default rather than an afterthought.


Engineering decisions

Structured AI output, not free text

Getting Claude to return confidence scores and reasoning in a consistent, parseable shape rather than freeform prose was the main design constraint on the prompt and response handling in lib/claude.ts. An LLM that occasionally answers in a different format to what the UI expects is worse than useless in a product context — the failure mode is a broken card, not a slightly-off paragraph — so the response handling has to be treated with the same rigour as parsing any other API contract.

Phased delivery, scoped honestly

The README tracks delivery in phases — fixture browsing and manual acca building first, then the Claude integration, with odds-weighted value badges and user accounts explicitly marked as not-yet-built rather than implied to exist. Being honest in the README about what's scaffolded versus complete is a small thing, but it's the difference between a demo that oversells itself and one that accurately represents where the project actually is.


Stack

Layer Choice
Framework Next.js 14 (App Router) · TypeScript
Styling Tailwind CSS (dark mode default)
AI Anthropic SDK (@anthropic-ai/sdk)
Data API-Football · The Odds API
Deployment Vercel

Reflections

The AI integration here taught me more about API contract design than about football. The genuinely useful part isn't "AI predicts football" — it's the discipline of turning a model's output into something a UI can trust: consistent shape, sane fallback behaviour, and a value calculation that's just arithmetic once the probability estimate exists. The demo-mode decision was a small one that paid off repeatedly — being able to run and show the app without three sets of API credentials made it far easier to iterate on and to hand to someone else to try.