I have been running a price-alert bot on Polymarket's public API since October 2025. Here is every place it gave me a wrong answer without ever returning an error.
slug = will-joe-biden-get-coronavirus-before-the-election GET /markets?slug=… 200 OK [] GET /markets?slug=…&closed=false 200 OK [] ← same as the default GET /markets?slug=…&closed=true 200 OK [ { … } ] ← there all along
The default query is not unfiltered. It is silently closed=false. So the API has no way to express “give me this market, whichever state it is in”. You have to already know whether it resolved in order to write a query that finds it. Ask wrongly and you are told, in the only vocabulary available for it, that a market which settled in November 2020 was never there.
That is the pattern in all six of the following. None of them throws. None returns a 4xx you would notice, or a message explaining itself. Each one hands back a response that is shaped exactly like a correct answer, and is wrong. I found all six by measuring something else and noticing the numbers were impossible.
They are ordered by what they cost me.
I tried to check whether the markets my bot had flagged were eventually right. Of 14,090 markets I had alerted on, 13,312 came back empty. I concluded the historical record was being deleted, and built an entire second subsystem to capture outcomes at the moment markets settle, rather than depending on being able to look them up later.
That subsystem was the correct thing to build for other reasons. But the premise was wrong. Measuring it properly, by sampling my own alert history and asking for each market both ways:
| markets flagged in | plain query | with closed=true | genuinely gone |
|---|---|---|---|
| Oct 2025 | 0% | 69% | 31% |
| Nov 2025 | 0% | 80% | 20% |
| Dec 2025 | 0% | 72% | 28% |
| Jan 2026 | 0% | 93% | 7% |
| Jul 2026 | 41% | 55% | 5% |
| Aug 2026 | 10% | 90% | 0% |
Zero. Not “a low number”. Zero percent of the markets I flagged between October 2025 and January 2026 are visible to a default query today. They are all still there. Every one of them is one parameter away.
There is a real decay underneath it too: roughly 20–30% of ten-month-old markets are unreachable by slug even with closed=true. That is the genuine data-availability problem, and it was completely hidden behind the bigger, dumber one.
There is a second-order cost to this one. Because the default silently excludes resolved markets, a market that is still open but whose end date has long passed is also easy to misread in the other direction, and there are a lot of those. 6,643 of the 36,476 markets Polymarket currently lists as active have an end date in the past. A Japanese baseball game scheduled for 4 July is still flagged open on 22 August. Whatever “active” means on this exchange, it is not “has not finished yet.”
closed take an "any" value, so a client that does not already know a market's state can still ask about it.The offset cap sits just above 2,000. My pagination loop treated a non-200 as the end of the data and stopped, which is a normal thing for a pagination loop to do. So the bot silently narrowed to about 1,100 markets when roughly 3,600 qualified. It kept running. It kept posting. It was simply blind to two-thirds of the exchange, and the only symptom was that it had less to say.
The message naming the fix was in the response body the entire time. I was reading status codes and not bodies. That part is my fault and I would rather say so than not. But a cap that announces itself only to whoever thinks to print the body of a failure is a cap most people meet the hard way.
I take a daily census of the whole catalogue. My own code kept whatever it had collected when a page failed. That is correct for alerting, where a short list just means fewer candidates. For recording, it is poison. The census wrote down that Polymarket had 2,400 markets on one day and 2,400 again the next, against a real catalogue of 36,464. Sports appeared to fall from 4,758 markets to 45 overnight.
Nothing had happened. One request in a 365-page walk had been throttled. Two days of a table whose entire purpose is measuring change over time recorded a change that never occurred.
The endpoint the first one redirects you to wraps its rows under markets. If you migrate and reach for data, the more common convention and the one I assumed, you get undefined, then an empty array, then a loop that exits immediately having found nothing. No error at any step.
Walking resolved markets to build an outcome dataset returns 2020 and 2021 first. Any sane safety limit on the page count (mine was 200 pages) terminates the walk while it is still years away from anything recent. You end up with a large, clean, perfectly useless dataset that joins to none of your current markets.
Identical request, identical parameters. The 403 carries no explanation, so it reads as an auth problem or a block, not a user-agent filter. Python's standard library is a reasonable guess at the first thing a newcomer reaches for.
The common thread is that none of these are outages. Every one returned a response that a reasonable client accepts. Empty arrays, valid JSON with the rows under a different key, a truncated walk that looks like a finished one, a 200 that means no. A developer who hits any of them does not file a bug, because from where they are standing nothing has broken. They build something quietly wrong, get confused by their own numbers, and drift away.
I only found them because I was measuring the exchange and kept arriving at figures that could not be true: a catalogue that shrank 94% overnight, a bot that went quiet with no error in its logs, sports markets that vanished and came back. Every impossible number turned out to be my instrument, and every instrument turned out to be reading a response that said it was fine.
Fixing the first item alone, a 404 where there is currently an empty list, removes the single most expensive misconception I have had about this platform in ten months.
Every example above re-verified against the live API on 22 August 2026.
Gent Blaku · x.com/_GentB · linkedin.com/in/gentblaku
The bot is @p0lywhales. Not affiliated with or endorsed by Polymarket.