Couchcall

How it works · the recipe, the sources, the skill

Build your own Couchcall

Couchcall is a few thousand lines of plain Python, a SQLite file, a static site and a scheduled job. Nothing in it needs a language model or an application server. This page is the recipe, the kinds of sources you can build on, and how to put an agent skill on top, so you can ask it questions in plain language.

Start with a prompt

The fastest way in is to hand the job to a coding agent, but the result is only as good as what it knows about you. This prompt makes the agent interview you first: which leagues, what makes a game good for you, when you watch and how strict you are about spoilers. Only then does it choose sources and write code. Paste it into Claude Code or any agent that can run commands on your machine.

I want to build my own version of Couchcall (https://couchcall.app/how/diy/): a small, deterministic pipeline that rates upcoming games by how worth watching they are and rates finished games without spoiling them, with an agent skill on top so I can ask about it in plain language.

Before you write any code or choose any data source, interview me. Ask one or two questions at a time, wait for my answers, and keep asking follow-ups until you could explain back to me exactly what I want and why. Cover at least:

1. Sports and leagues: which ones, and which matter most. Teams I support, and whether to include them or leave them out (a neutral view of your own team is hard).
2. What makes a game worth my time: a close contest, star players, tactics, lots of goals or points, rivalries, what is at stake in the table, physical play, underdogs, late drama. Ask me for two or three games I loved and one I regretted watching, and what made the difference. If I need prompts, https://couchcall.app/fans/ describes seven kinds of fan.
3. When and where I watch: my time zone, the hours I am free, the country I am in and the streaming services I already pay for.
4. Spoilers: how strict. Is a one-word verdict such as "worth it" fine, or should even that wait until I ask?
5. How I want to use it: a daily digest, a weekly page, answers in chat, calendar entries or notifications.
6. What I can run: my own computer only, or a small scheduled job somewhere, and whether I will pay for data.

Then write what you heard as a one-page spec and ask me to correct it. Only after I agree, propose the data sources for my leagues (open feeds first, paid ones only where nothing open exists, and check each source's terms), the scoring terms and weights that fit my answers, and the commands the skill will expose. Keep the scoring in plain code, not in the model, so the same game always gets the same number, and never let the skill show a final score unless I ask for spoilers.

Answer in detail. “Close games” gives the agent little to go on; “I stop watching when a team leads by two goals before the hour, but I will watch a 0-0 between two pressing sides” tells it how to weight closeness against tactics, and which games to rate as worth the replay.

The shape

  1. Ingest. Fixtures, prices and ratings once a day, results every hour: fixtures and results for each league into a games table, prices into a market table, ratings and tables into context. Keep the raw responses on disk so a parser change is a re-run, not a re-fetch.
  2. Score. For every upcoming game, seven numbers between 0 and 1 (closeness, quality, stakes, rivalry, pace, form and upset spice), combined into one weighted sum and stored with its components so the page can show its working. The weights.
  3. Verdict. For every finished game, an excitement number from how the game moved, turned into stars and into three words. Store the final score with the verdict, but never print the final score where the verdict is shown.
  4. Publish. Render every page as static HTML, check the tree (no spoiler words outside disclosures, unique titles, one heading, an image), and upload. A scheduler runs the whole thing hourly for results and once a day for the rest.

Sources you can build on

Our own mix is still changing: a blend of open feeds and paid APIs, swapped as one proves better or cheaper than another, which is why the pages describe the kind of source rather than a name, Wikipedia aside. These are the kinds of places to look, by what you need.

Fixtures, results and tables

Most leagues publish a schedule and a table on their own site. Check the terms before reading anything the site does not offer as a feed. Open football data projects cover the big European leagues with free keys. American leagues have public stats APIs (the NHL's is open and widely used; college football has a community data project with a free tier). Some large sports sites are driven by undocumented JSON endpoints; read their terms first, because they can change or block access without notice. Wikipedia's league tables are structured enough to parse for the small leagues nothing else covers; its licence asks for attribution, so credit it on the page.

Prices

You need a win probability, and a bookmaker consensus is the best cheap one. Some odds aggregators offer a free monthly allowance; pulled once a day and gated, it can cover a dozen leagues. Keep every price as a probability with the margin removed and never link to a bookmaker, so the page stays a guide to watching and not to betting. Where there is no market, points per game or an Elo rating gives a fallback, and the page should say so.

Strength ratings

Public Elo tables exist for club football and for several other sports, and the Elo sites publish CSVs. American football has published power ratings and playoff probabilities. The league table itself, pulled towards the average early in the season, is a sound rating where no other is published.

What happened in the game

The spoiler-free verdict needs a record of how the game moved: a win-probability series where one is published, otherwise a goal timeline with minutes. Look at match pages on league sites, open football data projects (events with minutes) and hockey leagues' play-by-play, and use the box score as a last resort (score only, and say so).

TV listings

Rights are sold by league and country, usually for several seasons at a time, and the details shift every season, so keep a small hand table with a review date per row and a form for corrections. Broadcaster schedules and the leagues' own 'where to watch' pages are the sources, and Wikipedia's broadcaster lists are a reasonable monthly cross-check.

Conventions worth copying

An agent skill on top

Our own day-to-day use of the pipeline, away from the site, is a conversation: what is on tonight, was that game worth the replay, make me the weekend page. That works best as a skill, a short Markdown file an agent loads when the question matches, which tells it where the project is, which command answers which question, and the rules it must keep (spoiler-free, no invented games). This is a trimmed version of ours:

---
name: couchcall
description: Which games are worth a neutral's evening? Runs the local pipeline to rank upcoming games and rate finished ones spoiler-free. Use when I ask what to watch, whether last night's game is worth the replay, or to run the daily update.
---

Project directory: ~/work/couchcall (Python 3.12, SQLite at data/couchcall.sqlite). Read README.md if unclear.

| Ask | Command |
|---|---|
| Daily update | `--league all update` |
| What to watch this weekend | `digest --from <fri> --to <sun> --top 10` |
| Was last night's game good, spoiler-free | `replay --from <date> --to <date>` |

Rules: past games stay spoiler-free in every answer: name the game, its verdict, kickoff and channel, in kickoff order, never a score, winner, margin or comeback. Answer from the tables; if a source fails, say so and do not invent games. After I watched a game, append a line to data/notes.md with the date, game, our score and my verdict in a few words.

Three things make it work. The skill names commands, not procedures, so the agent runs the deterministic code and only phrases the answer. The spoiler rule is stated in the skill and enforced in the code, so the model is never handed a score to leak. And the last line turns watching into data: every game you rate by hand becomes a row for refitting the weights.

To tailor it, change three files: the league configuration (time zone, convention, which signals exist, promotion and relegation places), the rivalry CSV, and the skill's table of asks. A new league is one configuration entry, one rivalry CSV and, if no existing adapter covers its data, one adapter. The pages, feeds, images and checks follow.

python3 -m couchcall.cli --league all update
python3 -m couchcall.cli --league nhl replay --from 2026-10-27 --to 2026-10-28