A live results page is only as good as the feed underneath it, and those feeds are far stranger than the polished pages built on top of them suggest. Over the past year I have wired up quite a few. Three of them illustrate the whole range.

The good case: a broadcaster's JSON service

Iceland's 2026 referendum looked, at first, like a dead end. The national electoral commission published results as a web page for humans and nothing machine-readable at all.

The public broadcaster did. RÚV's election site is a single-page application, and single-page applications have to get their data from somewhere — in this case a JSON service that returned each constituency's running totals, with a flag saying whether the count there was final. That flag turned out to be the most important field in the entire feed: it is what gates whether we are allowed to publish a turnout figure or a verdict.

The generalisable lesson is one I now apply first: before concluding a country has no feed, look at the broadcaster. National broadcasters cover election night at enormous expense, they are fed by the same returning officers as the commission, and their front-end almost always sits on an API that is easier to consume than anything the commission publishes.

The strange case: results as protocol buffers

Poland's electoral commission, the PKW, runs one of the better election-night systems in Europe. It also does not serve JSON.

Requesting the obvious JSON endpoint returns the single-page application's own HTML — with a 200 status code, which is the worst possible failure, because naive code will happily parse the HTML as if it were data and produce nothing but silence. The actual results are served as protocol buffers: compact binary blobs designed for machine-to-machine transfer, with no field names in the payload at all.

Binary without a schema sounds like a wall. It is not, because the schema has to exist somewhere the browser can reach — namely in the site's own JavaScript bundle, where the decoding functions carry the field numbers and their names. Reading that bundle gives you the message layout; from there the blobs decode cleanly.

The verification step that matters: a decoder written this way is a hypothesis until you run it against an election that has already finished and whose official numbers are published. Ours was checked against a completed 2025 mayoral count and reproduced it to the individual vote before it was allowed anywhere near a live election. A parser validated only against live data is a parser you are debugging in public.

The bad case: a portal that published nothing

Zambia's 2026 general election is the one that taught me the most, because the feed simply never arrived. The electoral commission's results portal was live throughout the count and, through the entire week, published no constituency results at all. The count that decided the presidency and 226 parliamentary seats went out over television and radio, read aloud, constituency by constituency.

Two things follow from that. The first is practical: a live page must degrade honestly. Ours kept showing a waiting state rather than inventing progress, and the final figures — Hakainde Hichilema on 60.49%, the UPND with 136 of 226 seats — were entered from the announced results once they existed.

The second is a caution about scraping. A results portal is a website, and websites contain markup that is not the result: sample rows left in a template, placeholder values in hidden elements, tables that are populated only later. Automated readers, including search engines, do sometimes surface those values as though they were the count. If a number cannot be traced to a stated, final, official figure, it does not belong on a results page — no matter how officially-branded the page it came from looks.

What all three have in common

None of these feeds was documented. There is no published specification for any of them; every one was reverse-engineered from what the source's own front-end does. That is normal, and it has consequences for anyone building on top:

  • Assume the shape can change without notice, including in the middle of the count. Parse defensively and fail closed.
  • Absence is not zero. In the PKW protocols, a candidate who took no votes at a given polling station has no entry rather than a zero — and summing a missing field without a default turns a running national total into nonsense. That specific bug has bitten me.
  • Do the work early. The Polish feed was decoded and validated four weeks before polling day, and nothing had to be fixed on the night. Iceland's was found on the morning of the vote, and three fixes shipped mid-count.

The visible part of election night is a page updating every few seconds. The part that determines whether it is right was finished weeks earlier, or is being written in a panic at eleven at night. I have done both, and the difference is entirely in the preparation.