A polymarket arbitrage bot is not a clever-idea project; it is a latency-and-discipline project. The five real arbitrage patterns on Polymarket are well-known, the edges are tight (typically 0.5 to 3 cents per share net of fees and gas), and the windows close in under four minutes. Any reader who has shipped an exchange integration before will recognise the architecture immediately: a multi-market websocket listener, a pattern detector tuned to those five shapes, a latency-budget filter, a two-leg order coordinator, and a confirmation tracker. This guide walks the full build to ship a working polymarket arb bot on Polygon, with code excerpts in Python, a latency-budget diagram, a pattern-economics table, and a candid section on what a retail-scale arbitrage bot polymarket builders ship cannot do. The aim is a build polymarket arbitrage bot reference that respects how execution-bound this game actually is.
What “arbitrage bot” means on Polymarket
Before any code, the builder has to know which arbitrage patterns are real and which are noise. The 2026 Polysyncer pattern study, summarised in the 2026 Polymarket arbitrage map, identifies five repeatable shapes that account for the vast majority of capturable edge. A polymarket arbitrage bot worth deploying targets a subset of these explicitly; trying to capture all five with one codepath is the most common reason first builds fail.
- YES/NO same-market. The cheapest and most common pattern. The YES and NO outcome tokens on a single binary market should sum to one dollar net of fees; when the sum drifts below 0.985 or above 1.015, a two-leg trade locks in the spread. Closes in 30 to 120 seconds.
- Mutually-exclusive set. A multi-outcome market (for example, “which candidate wins the primary”) where every outcome token must sum to one dollar. When the sum drifts, buying or selling the basket captures the spread. Closes in 90 to 240 seconds.
- Conditional / parent-child. A child market (“X wins given Y happens”) and its parent (“Y happens”) mispriced relative to the unconditional market on X. Closes in 2 to 10 minutes.
- Cross-platform. Polymarket vs Kalshi, or Polymarket vs a regulated sportsbook, on the same underlying event. Edges are larger (often 3 to 8 cents) but execution is harder because the legs are on different venues with different settlement times.
- Resolution-window decay. As a market approaches resolution and the outcome is informationally locked, lingering quotes on the losing side decay slowly. A bot can sell the losing side near zero against the obvious winner.
A retail-scale arbitrage bot polymarket operators ship is usually scoped to patterns one and two. Cross-platform requires a Kalshi or sportsbook integration that doubles the engineering surface; conditional arbitrage requires market-graph modelling that is its own multi-week project. This guide focuses on the YES/NO and mutually-exclusive patterns, with notes on what changes for the other three.
Why arbitrage on Polymarket is execution-bound
The defining property of polymarket arbitrage bot work is that ideas do not matter. Every serious participant on the platform knows the five patterns. The order books are public, the API is uniform, and any half-decent scanner finds the same opportunities within seconds of each other. The bot's job is not to discover edge; it is to capture a fraction of edge that is already visible.
That reframes the entire engineering problem. The bot is not a research tool. It is a low-latency, high-discipline execution system whose value is decided by four numbers: time from quote-update to detection, time from detection to signed order, time from broadcast to confirmation, and the share of two-leg attempts where both legs actually fill. A build polymarket arbitrage bot that hits 800 milliseconds end-to-end and a 92 percent both-legs-filled rate will print money in the patterns it targets. A build that hits 3 seconds and 70 percent will lose money because the single-leg failures swamp the captured spread.
Edge per share is small. After CLOB taker fees (0.75 percent on each leg) and Polygon gas on settlement, a YES/NO arbitrage with a 1.5-cent gross spread leaves roughly 0.6 cents per share net. To make $50 a day at that edge, the bot has to fill 8,000 shares of qualifying patterns. That volume only exists if the bot is fast enough to win against the half-dozen other arbitrage bot polymarket teams scanning the same books.
The architecture — five components
The bot decomposes into five components, each a separate Python module, communicating through asyncio.Queues. The shape mirrors a generic execution bot but with two new components specific to arbitrage: the pattern detector and the order coordinator.
Polymarket arbitrage bot — latency budget per stage
The five components:
- Multi-market WebSocket listener. Subscribes to every market in the candidate universe (typically 200 to 800 active markets) and maintains a cached top-of-book for each. The listener is the source of truth for “current state of every relevant book.”
- Pattern detector. On every book update, evaluates whether the affected market participates in any of the five patterns and, if so, whether the current quotes constitute capturable edge. Detection runs in under 20 milliseconds because most updates do not produce signals.
- Latency-budget filter. Even when the math says a pattern is capturable, the bot has to confirm the historical close-rate for patterns of this type at this size leaves enough budget for round-trip execution. Patterns whose median close-time is under the bot's worst-case latency are skipped.
- Order coordinator. Builds, signs, and submits both legs of the arbitrage near-simultaneously (within 50 milliseconds of each other). On Polymarket the legs are independent CLOB submissions, so the coordinator manages a small state machine for partial-fill recovery.
- Confirmation tracker. Watches for fill events on both order IDs. If one leg fills and the other does not within 10 seconds, triggers the single-leg recovery path (covered under pitfalls).
Pattern detection in code
The simplest pattern — YES/NO sum-below-1 — is also the most instructive. The detector takes the cached top-of-book for both outcome tokens on a binary market and computes whether buying both at the ask, or selling both at the bid, locks in a guaranteed return after fees.
# detector.py — YES/NO arbitrage signal for build polymarket arbitrage bot
from dataclasses import dataclass
TAKER_FEE = 0.0075 # 0.75% CLOB taker fee per leg
MIN_NET_EDGE = 0.006 # require 0.6 cents per share net
@dataclass
class BookTop:
bid: float # best bid price
bid_size: float # size at best bid
ask: float # best ask price
ask_size: float # size at best ask
def yesno_arbitrage(yes: BookTop, no: BookTop):
# Pattern A: sum of asks below 1 — buy both, collect 1 at resolution
buy_cost = yes.ask + no.ask
gross_a = 1.0 - buy_cost
net_a = gross_a - TAKER_FEE * buy_cost # both legs are taker
# Pattern B: sum of bids above 1 — sell both (requires inventory or short)
sell_credit = yes.bid + no.bid
gross_b = sell_credit - 1.0
net_b = gross_b - TAKER_FEE * sell_credit
max_size = min(yes.ask_size, no.ask_size) if net_a > net_b else \
min(yes.bid_size, no.bid_size)
if net_a >= MIN_NET_EDGE and net_a > net_b:
return ("buy_both", net_a, max_size)
if net_b >= MIN_NET_EDGE:
return ("sell_both", net_b, max_size)
return None
Two things are worth pointing out. First, the function returns the minimum of the two top-of-book sizes; sweeping deeper levels is possible but each level has its own price and the net edge calculation gets messier. For a first build, cap at top-of-book size. Second, the function assumes both legs are taker fills. On a quiet market the bot can post the slower leg as a maker order and skip the fee on that side, but this introduces fill-time uncertainty that often outweighs the saving. Start with both-legs-taker and optimise later.
For the mutually-exclusive set pattern, the detector generalises to an N-outcome sum check. The same edge threshold applies, but the coordinator now has to fan out N near-simultaneous submissions, which is the main reason mutually-exclusive arbitrage has lower both-legs-filled rates than YES/NO.
Order construction and the coordinator
Once a signal fires, the coordinator builds two EIP-712 signed orders and submits them. The critical property is that the two submissions overlap in time: if leg A is submitted, fills, and only then is leg B submitted, the bot has been directional for several hundred milliseconds and the book has moved.
# coordinator.py — two-leg submit for arbitrage bot polymarket
import asyncio
from clob_client import sign_order, submit_order # see py-clob-client
async def fire_two_leg(signal, wallet, size):
direction, net_edge, _ = signal
if direction == "buy_both":
leg_a = sign_order(wallet, market=signal.yes_id, side="BUY", size=size, price=signal.yes_ask)
leg_b = sign_order(wallet, market=signal.no_id, side="BUY", size=size, price=signal.no_ask)
else:
leg_a = sign_order(wallet, market=signal.yes_id, side="SELL", size=size, price=signal.yes_bid)
leg_b = sign_order(wallet, market=signal.no_id, side="SELL", size=size, price=signal.no_bid)
# near-simultaneous submission — gather, not sequential await
results = await asyncio.gather(
submit_order(leg_a, relay="private"),
submit_order(leg_b, relay="private"),
return_exceptions=True,
)
return leg_a, leg_b, results
Pre-signing both orders before asyncio.gather is what keeps the inter-leg gap under 50 milliseconds. Signing inside the gather (one signature per task) typically blows the gap out to 200 milliseconds because the signing crypto is CPU-bound and the tasks contend on the same event loop.
The latency budget — where milliseconds go
The chart above and the table below break down where time is spent. The numbers come from instrumenting a production arbitrage bot polymarket deployment over several weeks; the pattern close-times are from the 2026 study.
| Pattern | Typical close-time | Gross edge | Net edge after fees | Latency budget |
|---|---|---|---|---|
| YES/NO sum-below-1 | 30–120 s | 1.0–2.5¢ | 0.4–1.7¢ | < 2 s round-trip |
| Mutually-exclusive basket | 90–240 s | 1.5–3.5¢ | 0.6–2.4¢ | < 3 s round-trip |
| Conditional / parent-child | 2–10 min | 2–5¢ | 1–3.5¢ | < 6 s round-trip |
| Cross-platform (Kalshi) | 5–30 min | 3–8¢ | 1.5–5¢ | < 15 s round-trip |
| Resolution-window decay | 10–60 min | varies | varies | relaxed |
Two practical takeaways. First, the latency-budget filter should reject any pattern whose median close-time is under 3× the bot's p99 round-trip. A bot with a 2-second p99 should never attempt patterns that close in under 6 seconds, regardless of nominal edge. Second, the table is why retail builds focus on YES/NO and mutually-exclusive: the budgets are generous enough that a Python build polymarket arbitrage bot can compete; the under-6-second budgets on conditional patterns require Rust or a co-located deployment.
MEV protection and private relays
Submitting orders through the public CLOB endpoint is fine for under-$200 trades. Above that, the bot becomes worth front-running: a sophisticated observer watching the public Polygon mempool can detect the resulting fill, infer the arbitrage opportunity, and place their own competing legs. The mitigation is a private bundle relay — Flashbots Protect on Polygon, Merkle.io, or an equivalent — that hands the transaction directly to a trusted block builder without broadcasting it to the public mempool. The cost is typically 0 to 30 dollars per month plus a slight increase in inclusion latency (50 to 150 milliseconds).
For a polymarket arb bot the trade-off is one-sided once trade size justifies it. The 100 milliseconds of added latency is well within the YES/NO budget; the protection against being raced is essential because the bot's edges are too small to absorb even one front-run per hour. Polysyncer's executor implements many of these patterns and routes through private relays by default above the size threshold.
Common pitfalls
Four pitfalls account for the majority of failed arbitrage bots.
Single-leg fills. The defining failure mode of any two-leg arbitrage bot. Leg A fills, leg B's quote is gone by the time the submission lands, and the bot is now sitting on a one-sided directional position with no edge. The recovery path is non-optional: detect the unmatched leg within 10 seconds, attempt to fill the other side at market within a configurable slippage tolerance, and if that fails, unwind the filled leg. In a year of running, expect single-leg events on 3 to 8 percent of attempts even with a well-tuned bot.
Stale order books. The pattern detector reads from the cached top-of-book. If the websocket dropped two updates ago and reconnect is in progress, the cache is stale and the detector will signal on phantom edges. Tag every cache entry with the timestamp of the last update; reject any cache older than 1.5 seconds.
Gas spikes during congestion. Polygon settlement gas is normally cheap but spikes during congestion. The arbitrage edge is tiny enough that a 10× gas spike turns a profitable trade into a small loss. Check current gas before submitting and abort if it exceeds a ceiling. The full reasoning is similar to the gas pitfall in the general trading bot build, but the threshold for an arbitrage bot polymarket should be tighter because the per-trade edge is smaller.
RPC failover. The bot needs two independent RPC endpoints with sub-2-second failover. A single endpoint will fail multiple times per day. The order coordinator should treat RPC error as a signal to abort the second leg if the first has not yet been submitted, and to escalate to the recovery path if the first has been submitted.
Production hardening
Once the bot is profitable on paper, the production hardening list is short but non-negotiable.
Multi-region failover. Run two instances in different AWS regions (us-east-1 and eu-west-1) with a shared lock so only one is the leader at a time. If the leader heartbeat stops, the standby takes over within 15 seconds. The cost is doubled infrastructure ($40 to $80 per month) and the benefit is no single-region outage takes the bot down during a high-volume event.
Alerting on stuck positions. The confirmation tracker should emit a critical alert if any single-leg state persists for more than 30 seconds. The alert routes to PagerDuty or a Telegram bot; the operator either confirms automatic unwind or intervenes manually.
Automatic unwinding. The single-leg recovery path should default to automatic. Manual intervention is for cases where the recovery itself fails (typically because the market has gapped). The unwind logic and its limits are the same shape as any execution algo's emergency-stop logic.
Pre-trade liquidity check. Before signing either leg, re-read the order books from the cache (or, for paranoia, from a fresh REST snapshot). If either book has moved against the trade by more than half the captured edge, abort. This burns 10 to 30 milliseconds and saves on perhaps 5 percent of attempts that would otherwise be marginal. The full liquidity context is mapped in the 2026 liquidity map.
What this is not a substitute for
A retail-scale polymarket arb bot built to this guide is a real working system and can produce real returns. It is not a substitute for a professional arbitrage desk. Professional desks run co-located infrastructure in the same data centre as the gateway, write their hot path in Rust or C++, have direct relationships with block builders for guaranteed inclusion, and capture patterns this bot does not see — including conditional and cross-platform arbitrage at speeds inaccessible to Python. They also have the capital to absorb single-leg risk on much larger sizes.
A retail build polymarket arbitrage bot of the kind described here captures a fraction of patterns the desks see, in the patterns where latency is not the binding constraint, at sizes that are not worth their attention. That fraction is enough to be a worthwhile project for a competent engineer; it is not enough to be a career.
For builders who want the broader Polymarket trading-bot context (general copy-trade, market-making, alerts), the companion how to build a Polymarket trading bot tutorial covers the general execution pipeline; this one is intentionally scoped to arbitrage. For a snapshot of which leader wallets are running adjacent strategies and how copy-trade interacts with arbitrage flow, see how Polymarket copy-trading works. The general theory of arbitrage as a market mechanism is summarised on the Wikipedia article on arbitrage; the practical Polymarket and Polygon details are in the Polymarket API documentation and the Polygon documentation.
Frequently asked questions
How profitable is a Polymarket arbitrage bot?
A well-tuned retail polymarket arbitrage bot targeting YES/NO and mutually-exclusive patterns typically captures 0.5 to 2 percent monthly return on capital deployed, after fees and gas, at sizes between $2,000 and $20,000. Above $20,000 the bot starts to compete with professional desks and returns compress. Below $2,000 the fixed monthly infrastructure cost ($40 to $100) eats most of the edge. The numbers are highly dependent on execution quality, not on idea quality.
What is the latency target for a Polymarket arb bot?
End-to-end (websocket event to both legs confirmed) the budget is under 4 seconds against a pattern close-window of 45 to 240 seconds for YES/NO and mutually-exclusive arbitrage. The binding metric is p99, not mean; the bot should be engineered so that 99 percent of attempts complete inside 2.5 seconds. Conditional and cross-platform patterns require tighter budgets and typically push builders to Rust or co-location.
Do I need to write the bot in Rust?
For YES/NO and mutually-exclusive arbitrage on Polymarket, Python is sufficient. The bottleneck is network latency to the gateway and the Polygon RPC, not language overhead. For conditional or cross-platform arbitrage, where round-trip budgets fall below 6 seconds and detection logic becomes more complex, Rust is the production choice. Start in Python; rewrite the hot path in Rust only if profiling shows it is the binding constraint.
How do I handle single-leg fills?
Single-leg fills are the defining failure mode of a two-leg arbitrage bot polymarket builders ship. The confirmation tracker should detect any leg that has not filled within 10 seconds of submission, then attempt to fill the missing leg at market within a configurable slippage tolerance. If that fails, unwind the filled leg. Without this recovery path the bot accumulates directional risk and bleeds capital. Expect single-leg events on 3 to 8 percent of attempts even when well-tuned.
Is arbitrage on Polymarket against the rules?
Polymarket's terms of service explicitly permit automated trading and do not prohibit arbitrage. The platform actually benefits from arbitrage activity because it tightens spreads and aligns related markets. Cross-platform arbitrage (Polymarket vs Kalshi or sportsbooks) inherits the legal status of using each platform in the operator's jurisdiction; United States residents are blocked from Polymarket geographically. None of this is legal or tax advice; consult a qualified professional before deploying capital in a build polymarket arbitrage bot project.