TL;DR: Sweepstakes die because someone has to manage them. For our 47-person World Cup sweepstake, I built a Slack bot that pulls live match data from a football API, maps every team to the person who drew it, and posts a structured daily digest every morning at 8am UTC. Python, GitHub Actions, a Slack webhook, and a Google Sheet. Zero manual input once it’s running. This post covers how it works, why the architecture is the way it is, and what it means when someone who doesn’t follow football and doesn’t write Python builds something that runs reliably for 38 days.
Our team runs a 47-person sweepstake for the FIFA World Cup 2026. That’s 47 people across multiple time zones, each with a team they were randomly assigned, following a tournament they may or may not care about, checking a group chat that will go quiet within two weeks unless someone actively keeps it alive.
That someone is always whoever organised the draw.
They spend the next month fielding the same questions. Who won yesterday? What time does my team play? Where are we in the standings? And as soon as they miss a day or two, the energy drops. The bracket dies. The £5 everyone contributed becomes a memory.
I wanted to remove that person entirely. Not the sweepstake. Just the admin holding it together.
What we built is a Slack bot that posts every morning. Yesterday’s results, today’s fixtures, the current top scorer race, and every team tagged to the person who drew it. No one needs to check a separate site. No one needs to ask. The information arrives before the workday starts, then the conversation happens on its own.
Why Remote Sweepstakes Die After Week One
The sweepstake organiser is the single point of failure. They know the results, track the standings, and answer the questions. When their week gets busy, updates stop. When updates stop, interest drops. When interest drops, the group chat goes quiet. The whole thing collapses under the weight of one person’s availability.
This is the same structural problem that breaks any business process that depends on a human to remember it. As The Self-Managing Business puts it, systems that depend on memory will fail. The fix is not trying harder; it’s building the trigger into the system so it runs whether anyone remembers it or not.
A sweepstake is a perfect candidate. The data changes daily. The structure never does. Results in, summary out, delivered at a consistent time. There is no decision that requires a human anywhere in that loop.
Gallup data from 2025 shows that teams with structured recurring social touchpoints report 37% higher morale scores than those without them. The key word is structured. Ad-hoc updates that depend on someone’s memory are not a structure. A daily Slack post at 8am is.
Slack’s own research found that 85% of workers want to feel closer to remote colleagues, and that disengaged workers cost the US economy up to $605 billion in lost productivity each year. Running a sweepstake does not solve disengagement. But shared moments, when they land consistently, do something real. They give people a reason to talk that isn’t work.
What the Bot Actually Does
Every morning at 8am UTC, a Slack message posts to the sweepstake channel. It contains three sections.
The first is yesterday’s results: every match that was played, the final score, the stage label (group stage, round of 32, knockout), and a tag for each participant whose team was involved. If your team played, you see your name in the message.
The second is today’s fixtures: every match scheduled for the day, with kickoff times converted to a readable format and the relevant participants tagged against their teams.
The third is the current top scorer race: a running leaderboard showing whose teams are performing best in the tournament based on stage progression and wins, formatted as a short ranked list.
At the bottom, there’s a link to a Google Sheet where participants can see the full draw, the bracket, and the prize breakdown.
No one posts this. No one prepares it. The data comes in from the API, the participant mapping is hardcoded in the script, and the formatted message goes straight to Slack.
How It’s Built (In Plain English)
There are four components.
The Python script handles everything: calling the football API, reading the match data, mapping teams to participants, formatting the Slack message, and posting it via webhook. It runs once per day. The whole thing executes in under a minute.
The football API supplies live match data. For this build, football-data.org’s free tier covers the World Cup with a 10-calls-per-minute rate limit. That’s enough for a daily batch script with room to spare. The API returns fixtures, results, scores, and stage information in structured JSON. Alternatively, the openfootball/worldcup.json dataset on GitHub is a free public-domain option with no authentication required, which works well for anything that doesn’t need live scores during matches.
The Slack incoming webhook is how the message gets delivered. You create one in your Slack workspace settings, give it a target channel, and get back a URL. Your script sends a POST request to that URL with the message payload. No OAuth, no bot user, no permissions setup beyond the initial config. Slack’s own documentation covers this in a few minutes.
GitHub Actions runs the cron schedule. A YAML workflow file in the repository tells GitHub: run this Python script every day at 8:00 UTC. The API key and the Slack webhook URL are stored as GitHub Secrets, not in the code itself. Nobody reading the repository sees them.
The draw mapping is a simple dictionary in the Python file: team name maps to participant name. When a match result comes in, the script looks up both teams, finds the participants, and builds the tags. Nothing fancy.
Why GitHub Actions Is the Right Hosting Choice Here
The honest answer: it’s free and it requires no infrastructure.
GitHub’s free tier gives public repositories unlimited Actions minutes. A daily script that runs for under a minute uses roughly 38 minutes across the entire tournament. For a private repository, the free plan includes 2,000 minutes per month, which is more than sufficient. No server to provision. No cloud function to configure. No monthly bill.
There are a few things worth knowing. Scheduled workflows run in UTC, with no timezone configuration available, so build your cron expression with that in mind. GitHub’s documentation also notes that if a repository has no activity for 60 consecutive days, scheduled workflows are automatically disabled. For a sweepstake bot that runs through a 38-day tournament, that’s not a risk, but it’s worth knowing for anything you want to keep running after the tournament ends.
The other advantage is that GitHub Secrets handles credentials cleanly. The API key and the webhook URL live in the repository’s secret store. The workflow file references them as environment variables. Nothing sensitive ever touches the codebase.
What the Google Sheet Still Does Well
The bot handles the daily rhythm. The Sheet handles everything else.
Participants want to see the full draw: all 47 names, all 48 teams, who got what. They want to see the knockout bracket as it fills in. They want to know the prize structure. None of that changes daily. Trying to build it into the Slack message would make the message unwieldy. Trying to build it into the script would add complexity that isn’t worth maintaining.
The division of labour is clean: the bot is for what changes every day, the Sheet is for what people reference when they want context. The bot links to the Sheet in every post. Anyone who wants to go deeper can.
This is the same principle that makes a central knowledge hub work for any small team: keep the dynamic output in the channel where people already spend time, and keep the static reference in a place that doesn’t need to be updated daily. Don’t let the complexity of the reference layer slow down the delivery of the thing people check every morning.
The Principle Behind All of This
This is a small build. 47 people, one football tournament, 38 days. But the structure behind it is the same one that applies to any recurring process that shouldn’t require human input.
The guide The Self-Managing Business frames this as the core move in operational systemisation: identify what repeats without changing in character, and remove yourself from the loop. The data changes every day. The process of fetching it, formatting it, and delivering it does not. That part belongs to the system, not to you.
This is also why the build is as simple as it is. There’s no dashboard. No admin panel. No interface for managing the draw. The only user-facing piece is the Slack message and the Google Sheet, and neither of those requires any maintenance during the tournament. The complexity is in the setup, not in the ongoing operation, which is the right way to build any automation that needs to run without you.
Compare this to the birthday and anniversary workflow built on the same principle: a Make.com scenario that pulls from a Google Sheet and posts to Slack once a day. Different data, same architecture. You set it up, it runs, you don’t touch it again. When you start thinking about simple automations as things that compound, a bot like this stops looking like a one-off project and starts looking like one piece of a larger system.
The Part I Should Probably Mention
I do not follow football. I did not know the difference between the group stage and the round of 32 when I started this. I have never written a Python script from scratch.
I built this anyway, and it works.
That’s not a brag. It’s a data point. Building with AI assistance changes what’s achievable when you understand the architecture you’re trying to create but lack the technical execution skills. I knew what I wanted: a script that fetches data, maps it to people, formats a message, and posts it on a schedule. I did not know how to write that script. The gap between those two things is smaller than it used to be.
What you do need is a clear picture of what the system should do and why. If the architecture is muddy, the assistance doesn’t help. If the process is clear before you automate it, the execution is often faster than you’d expect.
The sweepstake runs. The team is engaged. Nobody is managing it.
What This Looks Like in Practice
For 47 people on a remote team, this is what it looks like in practice: a Slack message arrives every morning before most people start work. If your team played the night before, your name is in the message. You don’t have to go looking for it. If your team plays today, you see the time. The leaderboard tells you where you stand.
People reply in the thread. They tag each other. They commiserate when their team gets knocked out. The chat that would have died at week two stays alive through the final.
The organiser, for the first time, gets to just be a participant.
If you run a remote team and you’re thinking about running a sweepstake, this is the architecture. If you’re thinking about what else on your team runs the same way (daily, repeating, data in, structured output out), that’s the more interesting question. There’s a list of those. Most of them still have a person in the middle.
If you want help taking a process like this from concept to something that runs without you, the Work With Me page covers what that looks like.
Frequently Asked Questions
For a daily batch script that only runs once in the morning, football-data.org’s free tier covers the World Cup with a 10-calls-per-minute rate limit, which is generous enough for this use case. If you want no authentication at all, the openfootball/worldcup.json dataset on GitHub is a free, public-domain option that covers all 104 fixtures and results. It works well for anything that doesn’t need live in-match updates. If you wanted live goal alerts during matches, you’d need a paid API tier, but for a daily morning digest, the free options are more than sufficient.
Yes. If you have fewer participants than teams, you either assign multiple teams per person or leave some teams unassigned. The participant mapping in the script is a simple dictionary, so you adjust it however you like. With 47 participants and 48 teams, one team goes unassigned. The bot just doesn’t tag anyone for that team when it appears in results or fixtures. No extra logic needed.
Once a team is eliminated, they stop appearing in fixtures because there are none. Their results won’t show up in the daily digest either, because no matches involving them are scheduled. The bot naturally stops referencing eliminated teams without any change to the code. The top scorer race section will continue to show participants even if their teams are out, which can actually be nice: it gives people a reason to stay engaged even after their team is knocked out.
The architecture transfers directly. The football-data.org API covers UEFA Euro (competition code EC) and the Champions League (CL) on the same free tier. The only things you’d change are the competition code in the API call, the draw mapping dictionary, and the tournament dates in the GitHub Actions cron schedule. The rest of the script stays the same. The Google Sheet structure is also reusable across events.
For most setups, zero. Public GitHub repositories get unlimited GitHub Actions minutes, and a daily 1-minute script uses roughly 38 minutes across a 38-day tournament. The football-data.org free tier and the Slack incoming webhook are both free with no rate concerns at this scale. If you use a private repository on GitHub’s free plan, the 2,000 included monthly minutes are well beyond what this build needs. The only scenario where cost enters the picture is if you want a paid API for live in-match data, which this architecture doesn’t require.