Sneaker Raffles For Fans, Not Bots
The people who want a pair of hyped sneakers the most are often the least likely to get them.
The exclusive end of the sneaker market is a rubbish heap of scalpers and bot operators, all of them working to get between a fan and a pair of sneakers. A first-come-first-served drop rewards exactly them. Whoever has the fastest script wins, and the person refreshing on their phone does not.
A raffle takes that advantage away, which is why we were already running them. What we did not have was a way to run them quickly. Entries arrived in the tens of thousands a minute, faster than we could check them, and the bad ones got sorted out slowly.
So in 2022 we built a system for it. Entering had to be simple, the draw had to be genuinely random, the checks had to keep up with the entries, and none of it could fall over when everyone who wanted a pair arrived inside the same minute. Every decision after that was made for the fans, so the people who actually wanted the sneakers had a fair chance at them.
Built to be flooded
The earlier raffles had told us the shape of the load: a spike at the open, every entry needing checks, and the checks are where the real work is. All of it had to scale, so AWS and serverless were the natural fit, and territory the team already knew. We were on the Serverless Framework at the time, which kept the configuration manageable and the deploys boring.
The most important decision was to separate taking an entry from processing it, so each half could scale on its own. Small Lambdas accept the entry and put it on an SQS queue. That is all they do. Processing reads off the queue in batches, with a retry path for anything that fails partway through.
Splitting it that way means the database never sees the spike. We control the rate entries are processed at, rather than endlessly scaling the database to absorb whatever arrives in the first sixty seconds.
The checks started simple: deduplication and validation. Even that told us a lot about the entries we were getting. It was enough to sort them into buckets by how likely each one was to be a real sneaker fan, and how likely it was to be fraud. Chargebacks, duplicate wins, that sort of thing.
What load testing found
We wanted to test as close to real scale as we could, so we used k6 to simulate thousands of people entering at once.
It went more smoothly than expected, and two things came out of it. The entry Lambdas were a little slow to scale up from a cold start. Slow enough that some requests timed out, which meant people saw an error and never got an entry in. So we put provisioned concurrency in front of them and kept a minimum number warm and ready. That turned out to be the right call for the opening spike, which is the only part of a raffle where being slow is visible to the person entering.
The second was indexes. At larger volumes the processing side slowed down in a way our monitoring caught, and the fix was in the indexes rather than anywhere more interesting.
The first launch
The first live raffle on the new system was a surprise before it even opened. We had planned for one sneaker. It turned out to be five, one of them in junior sizes, which meant far more entries than we had modelled, because the same person will happily enter every raffle on offer.
We were still confident, and it held. The client took millions of entries and we processed all of them without an issue.
Picking the winners
Winners are drawn at random. The scoring does not choose the winners; it decides which bucket an entry lands in, and the buckets decide which entries the draw runs against. A score is not fixed at entry either. Post-processing can adjust it, which moves the entry between buckets, or reject the entry outright before the draw runs. Sizes are processed to the client's requirements and winners get an email.
In an ideal world we would have captured payment details up front, which would have made processing winners far simpler. Given the volume of entries and the way Shopify works, that would have meant long queues and people unable to enter at all, which is a terrible experience for everyone involved. So entry is a simple form, and winners get a draft order email to complete and pay.
It is a good compromise but it has a real cost. Not everyone checks out. After the first raffle we added SMS alerts alongside the email, to make sure winners knew they had won and needed to check out. Even so, after an allowed window we send further waves of emails to sell the sneakers out completely, and some people who were genuinely drawn as winners end up disappointed because they did not complete in time.
Then the bots turned up
High fives all round. Relatively speaking, everything had gone really well.
We assumed that was because the system was good. Partly it was. But it also went well because nobody had written a bot for it yet. The system was new and it took the scalpers by surprise, so on that launch we never had to deal with them at all.
The next raffle was another heavily hyped sneaker for a different client. Hundreds of entries a second at the open, settling into a steady few thousand a minute after the first rush. Another one launched without issue, we thought.
Then, about twenty minutes in, a second spike, back up at opening levels. That was the bots. Entry after entry, more and more garbage. They were not ranking well and they were not winning anything, but they were taking up processing time and resources we did not want to spend on them again.
Adding speedbumps
We had a much larger raffle coming, so we went after the fraudulent entries directly and added more post-processing checks to catch whatever got past the first pass.
One of several changes was a captcha on the entry form. It slows the bots down, and it hands back a score per user, which became another signal for bucketing entries and running the draw.
A captcha on its own was never going to be enough. It is a well known path with well known solutions for anyone writing bots. So we added our own honeypot-style checks and time-limited grants to the frontend, so an entry has to come from a page that was really loaded, recently. More layers, more speedbumps.
We added several other processing checks too, based on what we had watched people actually do to avoid being flagged as duplicate entries.
There is a tension in writing any of this down. A raffle should be transparent, and fans are right to ask how the draw works. But these checks only keep working while the people writing bots do not know exactly what they are. So we will say what each layer is for, and not where the thresholds sit.
The next raffle
The new measures did what we wanted. Several million entries came in, scored on two new signals. Then, a few minutes in, the bots arrived: over a thousand junk entries a second, around 200,000 in the worst minute, all of them thrown out.
If you are building one
Most of what we would tell you is in the story above, so here it is short.
What the fan sees
None of this is visible from the entry form. A fan fills in a few fields and waits for an email, and that is the whole product from where they are standing, which is how it should be. The queues, the buckets, the speedbumps and the indexes all exist so that the draw they entered is the draw they thought they were entering.
Which also means there is very little for anyone to judge it on. You cannot please everyone, and after a big draw social media fills with people certain the whole thing was fixed or back doored, and that some other brand ran their raffle properly. The irony is that the better run raffle is usually also ours, same platform, different client.
None of it stays finished either. The battle with the botters and scalpers is never over, and it keeps evolving. Every raffle teaches us something new about how they get in, and the checks change to match, so that the people who win are the real fans.
We are sneaker fans ourselves, which is why the work goes where it does. All of it is for the person entering: a form that answers on the first tap, a draw they are genuinely in, and an email that arrives when it should. Whether that gets believed on launch night is the one part we cannot build.
If you have a high demand product coming and want help launching it fairly, speak to us at [email protected].