Designing a results lookup that knows which jurisdiction it is answering
- 02-07-2026
- Business
- Alan Ingram
- Photo Credit: Supplied
Deceptively annoying API problem: serving draw results when the same game runs separately in different states, and a wrong answer is worse than no answer.
The problem
A keno results endpoint looks trivial until you remember that the same game number can exist in two jurisdictions at once, with different times and different results.
So, "what was draw 472" is not a well-formed question, but "What was draw 472 in New South Wales" is.
Any results system that does not carry jurisdiction as a first-class part of the lookup will eventually hand someone the right number for the wrong state, which in a results context is just a confident lie.
Model jurisdiction as a key instead of a filter
The common mistake is treating the state as a display filter bolted on at the end, where you fetch a result and then tag it with a region because it inverts the actual relationship. The jurisdiction is part of the identity of the draw, not metadata about it.
Practically, that means your result key is a composite. Something closer to (jurisdiction, game_date, draw_number) than a global draw_id that you later annotate. The moment jurisdiction lives in the primary key, a whole class of bugs becomes impossible to express because you cannot accidentally request a result without saying which state you mean.
The schema refuses to let you ask the malformed question.
This also keeps you honest at the storage layer. Two states running draw 472 on the same evening are two rows that never collide, rather than one row fighting over which result wins.
Endpoint rejecting ambiguity
A results API should treat a missing jurisdiction as an error but a default.
Defaulting is tempting, you pick the busier state and move on, but a silent default is exactly how someone in one state ends up reading another state's numbers without realising the substitution happened.
So the contract is blunt. No jurisdiction, no answer. A request like GET /results?date=...&draw=472 returns a 400 asking which jurisdiction, rather than guessing. It feels stricter than necessary until the first time it saves a user from acting on the wrong result.
A consumer-facing product like a tool that makes it easy to check keno results in Queensland or New South Wales only feels effortless to the user because the layer underneath refuses to be vague about which state's draw it is showing. The simplicity on the surface is bought with strictness underneath.
Results are immutable, so cache them immutably
One genuinely nice property here: a completed draw result never changes.
Once draw 472 in NSW is settled, that record is frozen forever. That is a gift, because it means past results are perfectly cacheable with effectively infinite TTL, and you can push them to the edge without worrying about invalidation.
The only moving part is the boundary between "not drawn yet" and "drawn" so the caching split is clean.
Historical results get long-lived immutable caching keyed on that composite identity. The current or upcoming draw gets a short TTL or a live channel, because that is the only value still in flux. Conflating the two, putting the live draw and the settled archive behind the same cache policy, is where people either serve stale "pending" states or hammer the origin for results that will never change again.
A reasonable shape
Pulling it together, a sane results service tends to look like this:
● Composite identity: (jurisdiction, date, draw_number) as the key everywhere, from storage to URL.
● Strict reads: jurisdiction required, ambiguous requests rejected rather than defaulted.
● Split caching: settled results are immutable and edge-cached, the live draw on its own fast-changing path.
● One source of truth per draw: a settled result is written once and never mutated, so every cache layer downstream can trust it permanently.
All of it is mostly the discipline of refusing to answer a question that does not specify enough to be answered correctly. In a results context, that discipline is the whole product because a fast wrong answer is strictly worse than a slightly slower right one.
Summary
The hard part of a results lookup is identity. Once jurisdiction is part of what uniquely names a draw rather than a label you attach afterward, the storage stops colliding, the cache strategy falls out naturally, and the endpoint can be strict in the one place strictness actually protects the user.







































