Polymarket API Arbitrage: Building the Scanner

The Short Answer
Building an arbitrage scanner against the Polymarket API takes four stages: pull the catalog, match each market to the same event on another venue, compare live asks with size rather than midpoints, then gate the result on fees, gas and resolution equivalence. Polymarket's read endpoints are public and free, so stage one is easy. Stage two is the hard part, because no shared identifier exists across venues. Predictefy collapses stages one and two into a single normalized call across 16 venues, leaving you to decide what to do with a qualified row.
Key Takeaways
- Polymarket's CLOB and Gamma read endpoints need no key, so discovery costs nothing but the code.
- Cross-venue matching is entity resolution on prose, not a lookup, and it is where most scanners quietly go wrong.
- Compare asks with size, never midpoints. A midpoint scanner reports opportunities that disappear at the ask.
- Order books key on the outcome, not the market, so a binary market has one book per side.
- Ask for the size you intend to trade: a gap that clears at 10 contracts often does not clear at 500.
- Signing stays in your process on both legs, so a compromised scanner cannot move funds.
What does a Polymarket arbitrage scanner need to do?
Four stages, and the effort is not evenly distributed.
| Stage | Difficulty | Why |
|---|---|---|
| Pull the catalog | Easy | Public endpoints, no key, well documented |
| Match events across venues | Hard | No shared identifier exists for a real-world event |
| Compare live depth | Moderate | Books are outcome-keyed and change constantly |
| Gate on cost and rules | Hard | Fees, gas, and whether both contracts settle identically |
Most tutorials cover stage one and three and stop. Those are the stages that produce a number on a screen. Stages two and four are what decide whether the number means anything.
Reading Polymarket directly
Polymarket runs two public read surfaces. Gamma serves the catalog and event metadata, and the CLOB API serves order books, trades and price history. Neither requires payment to read, and both are documented.
One practical warning before you reach for a client library. The most-starred Python client, py-clob-client, was published by Polymarket themselves and is now archived, with its last commit in May 2026. It still installs and reads fine, but there will be no patches when the API shape moves. Check the current state of the clients before building on one.
Why is cross-venue matching the hard part?
Because there is no ticker for a real-world event.
Equities have a symbol. Bonds have an ISIN. Prediction markets have whatever the venue called it that morning. The same rate decision appears on Polymarket with a slug and a conversational title, and on another venue with a different slug and a title written like a contract clause. Sometimes the dates differ. Sometimes one includes a qualifier the other leaves implicit.
Establishing that two markets describe the same thing is a matching problem with real error rates, and getting it wrong is not a small bug. A false match produces a scanner that confidently reports arbitrage between two contracts that will settle differently.
This is the stage where building it yourself costs the most, and where a normalized layer earns its place:
curl -s "https://data.predictefy.com/api/router/fetchArbitrage?contracts=100&limit=50&executableOnly=true" \
-H "Authorization: Bearer pk_live_YOUR_KEY"
router answers across all 16 served venues at once, which a cross-venue question requires. contracts is the size you actually mean to trade, and the answer changes with it. executableOnly=true returns only rows that passed every gate; drop it to also receive indicative rows, each carrying the reason it was not upgraded.
Comparing depth correctly
Two mistakes account for most wrong answers here.
Using midpoints. The midpoint is the average of best bid and best ask. Nobody trades there. You buy at the ask, so a scanner priced on midpoints has given away half the spread per leg before it starts. On thin markets, where the apparent gaps are widest, that is most of the edge.
Using only the top of book. Forty contracts available at the best ask does not help if you want five hundred. The rest fills worse as you walk up the ladder. This is why size has to be an input rather than an afterthought.
There is also a structural thing worth knowing. An order book is supposed to record real resting orders, and on some venues that is exactly what it is. On others the book is reconstructed from a pricing curve and arrives looking identical: same fields, same shape, bids and asks and sizes. If your scanner treats reconstructed depth as real, it will report size that nobody will fill. Check whether the response flags it.
One more detail that trips people up: books and candles key on the outcome, not the market. A binary market has one book per side, so you resolve which side you are asking about before you request depth. Shipping a comparison that reads the Yes book on one venue and the No book on the other is a real and common bug.
Gating the result
A price difference becomes actionable only after it survives four checks: live asks with size, both markets open, fees and gas on both legs, and resolution equivalence between the two contracts.
Until then the honest label is an indicative price discrepancy. That wording is not pedantry. It is the difference between a number your scanner observed and a trade you can place, and conflating them is how people lose money on a strategy that backtested beautifully.
Build your gates to fail closed. When one cannot be evaluated, mark the row indicative and record why, rather than upgrading it and hoping. A scanner that cannot say "I do not know" will eventually say something false with the same confidence it says everything else.
Executing both legs
Discovery and execution are separate concerns and keeping them apart is the safer design.
On Predictefy, orders are built server side, signed in your own process, and relayed back. No endpoint both builds and submits, and no hosted signing route exists, so a compromise of the scanner cannot move funds. Server-side spend caps of 100 USD per order and 1,000 USD per key over a rolling 24 hours apply independently of anything your code does.
The practical risks at execution time are worth planning for. One leg can fill while the other does not, leaving you directionally exposed. Partial fills leave you hedged on part of the position. And the price can move during signing, since a wallet signature takes seconds the book does not wait for.
Frequently Asked Questions
Do you need a Polymarket API key to scan for arbitrage?
Not for reading. Polymarket's Gamma and CLOB read endpoints are public, so catalog and order book data need no credentials. You need a wallet and signing setup only when placing orders. A normalized cross-venue layer needs its own key, and Predictefy issues those free.
How fast do prediction market arbitrage opportunities disappear?
It varies enormously by market type. Fast-moving sports contracts can close within seconds, while long-dated political markets can hold a gap for hours. The gaps that survive longest are usually surviving for a reason, most often thin depth or a difference in resolution criteria.
Can you run a scanner without writing code?
You can call the arbitrage endpoint directly with curl and read the output, which needs no application. Building something that watches continuously and reacts does need code, though the hard parts, cross-venue matching and executability gating, are already done server side.
Why does my scanner find more opportunities than I can trade?
Almost always midpoints. A scanner computing on midpoints reports gaps that vanish once you price against live asks with real size. Thin books amplify this, since a wide top of book can hide very little depth behind it. Add fees and the remainder usually disappears.
What does executableOnly actually filter out?
Rows that failed any live gate, including insufficient depth at size, a closed market, or costs exceeding the gap. It also filters rows where a gate could not be evaluated, since those stay indicative rather than being upgraded. Drop the parameter to see those rows with their reasons attached.