How to Build a Prediction Market Arbitrage Bot (2026)

The Short Answer
You can build a prediction market arbitrage bot by using Predictefy's API to find cross-venue market discrepancies, qualify them against live order books at a specific trade size, filter for executable opportunities and then pass approved trades into a separate risk and execution layer. The key endpoint is /api/router/fetchArbitrage. Unlike a basic price scanner, it does not treat every difference between two prediction markets as arbitrage: an opportunity only earns the arbitrage label after the required depth, market status, fee, resolution and net-edge checks pass.
Most prediction market arbitrage tutorials make the strategy look easier than it is.
They tell you to find a YES contract trading for 40 cents on one platform, find the corresponding NO contract trading for 55 cents somewhere else, add the two together and collect the difference from $1.
The math is easy.
Building a bot that can actually trade the opportunity is not.
A production prediction market arbitrage bot needs to identify equivalent contracts across different venues, verify their resolution rules, inspect real order-book depth, calculate the actual execution price for the intended size, account for fees, detect stale or unavailable books and handle the risk that one leg fills while the other does not.
That is where Predictefy's cross-venue API becomes useful. Instead of building separate matching, market-data and arbitrage infrastructure for every prediction market, your bot can work from one normalized layer and focus on strategy, risk and execution.
Key Takeaways
- The core Predictefy endpoint for a prediction market arbitrage bot is
GET /api/router/fetchArbitrage. - The
contractsparameter matters because depth and fees are assessed at the actual size you request. fetchMatchedMarketsis useful for discovering cross-venue price differences, but those differences are indicative and should not be treated as executable arbitrage.- Predictefy only labels a result
arbitragewhen its live execution qualification gates pass. - A serious bot should use live order-book depth rather than headline YES and NO prices.
- Execution should sit behind deterministic risk limits and a final market-state refresh.
- One-leg execution risk means prediction market arbitrage is not automatically risk-free even when the payoff math is correct.
What Is Prediction Market Arbitrage?
Binary prediction markets normally settle to either $1 or $0 per contract.
If you can obtain complementary positions on the same economic outcome for a combined cost below $1, there may be an arbitrage opportunity.
For example:
Buy YES on Venue A: $0.43 Buy NO on Venue B: $0.52 Combined cost: $0.95 Settlement payout: $1.00 Gross difference: $0.05 If both positions are filled in the intended size, both contracts truly resolve as complements and the costs do not eliminate the spread, one side should settle to $1 while the other settles to $0.
The simple gross return on capital in this example is:
($1.00 - $0.95) / $0.95 = 5.26% But that 5.26% is not automatically what the bot earns.
The real calculation needs to incorporate:
- the actual ask prices available at the requested size
- order-book depth
- venue fees
- settlement fees or commissions where applicable
- resolution compatibility
- execution failure
- price movement between the two legs
That difference between visible spread and executable arbitrage is the most important idea in this guide.
The Basic Arbitrage Bot Architecture
A useful bot separates discovery, qualification, risk and execution rather than treating them as one step.
| Stage | What Happens | Tool |
|---|---|---|
| Discover | Find cross-venue market relationships and price differences. | Predictefy matched markets |
| Qualify | Price both legs against live books at the intended size. | Predictefy arbitrage API |
| Filter | Apply your minimum ROI, settlement and liquidity rules. | Your strategy code |
| Risk | Check account, event and venue exposure. | Your risk engine |
| Refresh | Confirm the opportunity still exists immediately before trading. | Predictefy API |
| Execute | Build, sign and submit through a supported execution lane. | Execution service / venue |
| Monitor | Confirm both legs actually reached the expected state. | Your execution monitor |
This separation is important because finding a spread and successfully trading a spread are different problems.
Step 1: Get a Predictefy API Key
Predictefy's data API uses Bearer authentication.
Keep your key on the server:
PREDICTEFY_API_KEY=pk_live_your_key_here Requests use:
Authorization: Bearer pk_live_your_key_here Do not expose a private API credential in browser-side JavaScript.
You can reference the current Predictefy API documentation for the live contract and capability surface.
Step 2: Start With Matched Markets
Before you can calculate cross-venue arbitrage, you need to know which contracts should be compared.
This is harder than matching two titles that look similar.
Consider these two markets:
Will Candidate A win the presidential election?
Will Candidate A win the popular vote?
The wording is similar.
The payoff is not.
A bot that treats them as complementary positions may think it found arbitrage when it has actually opened two separate directional bets.
Predictefy exposes matched-market infrastructure for cross-venue comparison. A useful discovery request is:
GET /api/router/fetchMatchedMarkets?relation=identity&sort=priceDifference For example:
const response = await fetch( 'https://data.predictefy.com/api/router/fetchMatchedMarkets?relation=identity&sort=priceDifference', { headers: { Authorization: `Bearer ${process.env.PREDICTEFY_API_KEY}` } } ); const matched = await response.json(); The important part is what you do not do next.
Do not see a large priceDifference and immediately place a trade.
Matched-market price differences are useful for discovery, but they are still indicative. They do not by themselves include all of the execution, fee, depth and resolution-equivalence checks required to call the result executable arbitrage.
| Data | Meaning | Trade It Automatically? |
|---|---|---|
| Matched market | Two markets have a cross-venue relationship worth comparing. | No |
| Price discrepancy | A cross-venue price difference exists. | No |
| Qualified arbitrage | The live arbitrage assessment passes the required gates at the requested size. | Candidate only |
| Risk-approved arbitrage | Your own portfolio and execution rules also pass. | Eligible for execution |
Step 3: Use the Predictefy Arbitrage API
For most developers, this is the most important step.
Predictefy exposes a router-level arbitrage assessment:
GET /api/router/fetchArbitrage A useful request might be:
curl --request GET \ --url 'https://data.predictefy.com/api/router/fetchArbitrage?contracts=100&executableOnly=true&limit=5' \ --header 'Authorization: Bearer pk_live_YOUR_KEY' Or in JavaScript:
async function fetchArbitrage({ contracts = 100, executableOnly = true, limit = 5 } = {}) { const params = new URLSearchParams({ contracts: String(contracts), executableOnly: String(executableOnly), limit: String(limit) }); const response = await fetch( `https://data.predictefy.com/api/router/fetchArbitrage?${params}`, { headers: { Authorization: `Bearer ${process.env.PREDICTEFY_API_KEY}` } } ); if (!response.ok) { throw new Error(`Arbitrage request failed: ${response.status}`); } return response.json(); } Then:
const result = await fetchArbitrage({ contracts: 100, executableOnly: true, limit: 5 }); console.log(result.data); Why the Contracts Parameter Matters
This is one of the easiest details to overlook.
Arbitrage is size-dependent.
Imagine the best YES ask is 42 cents and the best NO ask is 53 cents.
That looks like a 95-cent combined cost.
But perhaps only 10 contracts are available at those prices.
The next 500 contracts may be available at:
YES: $0.47 NO: $0.55 Combined cost: $1.02 The arbitrage exists for a small trade and disappears for a larger one.
That is why fetchArbitrage accepts contracts. Predictefy evaluates depth and relevant costs at the requested size rather than assuming that the top quote can fill the entire position.
For example:
const smallTrade = await fetchArbitrage({ contracts: 100, executableOnly: true }); const largerTrade = await fetchArbitrage({ contracts: 1000, executableOnly: true }); Those two requests can legitimately produce different results.
What Predictefy Checks Before Labeling a Trade Arbitrage
A simple prediction market scanner might compare two displayed prices.
Predictefy's arbitrage qualification is stricter.
An opportunity only earns the arbitrage label when the required execution gates pass at the requested size.
| Check | Why It Matters |
|---|---|
| Live real asks | The bot needs prices it can actually attempt to buy against. |
| Market open | A theoretical price is useless if trading has closed. |
| Full depth | Both legs need enough liquidity for the requested contracts. |
| Verified fees | The spread must survive the actual applicable cost model. |
| Resolution equivalence | The markets need to represent compatible settlement outcomes. |
| Positive net edge | There must still be profit left after the modeled costs. |
If the requirements do not pass, the API can keep the result labeled as an indicative price discrepancy and expose machine-readable reasons explaining why.
Step 4: Read the Arbitrage Response Correctly
The most useful fields for a bot include:
| Field | What It Tells You |
|---|---|
label | Whether the row is arbitrage or an indicative price discrepancy. |
executable | Whether all qualification gates passed at the requested size. |
contracts | The position size used for the assessment. |
legs.buyYes | The YES purchase leg. |
legs.buyNo | The NO purchase leg. |
vwap | Volume-weighted average price for the requested fill. |
cost | Walked order-book cost for the requested fill. |
fee | Modeled verified fee for the leg where available. |
totalCost | Combined modeled cost including relevant fees. |
payout | The modeled $1-per-contract settlement payout. |
netEdge | Payout minus modeled total cost. |
roi | Modeled return for the qualified trade. |
reasons | Why a candidate failed qualification. |
asOf | Freshness of the books used for the assessment. |
A simple strategy filter could look like this:
function isTradeCandidate(opportunity) { const MIN_ROI = 0.02; const MIN_NET_EDGE = 5; return ( opportunity.label === 'arbitrage' && opportunity.executable === true && opportunity.roi !== null && opportunity.roi >= MIN_ROI && opportunity.netEdge !== null && opportunity.netEdge >= MIN_NET_EDGE && opportunity.reasons.length === 0 ); } Your thresholds will depend on capital size, latency, execution setup and risk tolerance.
Step 5: Understand Why Opportunities Fail
A useful arbitrage bot should not only know what passed.
It should understand why the other candidates failed.
Predictefy exposes machine-readable failure reasons. Depending on the candidate, those can include issues such as:
unverified_feesmarket_not_opensynthetic_bookbook_unavailableno_asksinsufficient_depthresolution_equivalence_unverifiedsame_venueno_positive_edge
This is useful for monitoring your bot.
If 80% of candidates fail because of insufficient_depth, lowering your minimum displayed spread will not solve the problem.
If most fail because resolution equivalence is unverified, your bottleneck is market matching rather than liquidity.
A production system should store these reasons for later analysis.
Step 6: Inspect Live Order Books When You Need More Control
The arbitrage endpoint already performs the live qualification work required for its result, but you may still want raw order books for your own strategy logic or UI.
Predictefy exposes:
GET /api/{exchange}/fetchOrderBook?outcomeId=OUTCOME_ID For example:
async function fetchOrderBook(exchange, outcomeId) { const response = await fetch( `https://data.predictefy.com/api/${exchange}/fetchOrderBook?outcomeId=${encodeURIComponent(outcomeId)}`, { headers: { Authorization: `Bearer ${process.env.PREDICTEFY_API_KEY}` } } ); if (!response.ok) { throw new Error(`Order book request failed: ${response.status}`); } return response.json(); } A normalized order book gives you bids and asks with prices and sizes.
That lets you answer questions such as:
- How much size exists at the best ask?
- How quickly does price deteriorate deeper in the book?
- How much slippage should I expect?
- Is one leg materially thinner than the other?
Why VWAP Matters More Than the Best Price
Suppose the YES book looks like this:
| Ask | Contracts Available |
|---|---|
| $0.41 | 20 |
| $0.43 | 100 |
| $0.47 | 500 |
If your bot wants 500 contracts, it cannot use 41 cents as its execution price.
It has to walk multiple levels of the book.
The useful number is the volume-weighted average execution price, or VWAP.
Predictefy's API also exposes a stateless execution-price calculator:
POST /api/{exchange}/getExecutionPrice It takes a supplied order book, side and size and returns the VWAP needed to fill that amount, or zero when the size cannot be fully filled.
For most arbitrage applications, however, fetchArbitrage is the simpler route because it already evaluates both legs at the requested contract size.
Step 7: Add Your Own Strategy Filters
Just because an opportunity qualifies as executable arbitrage at the API layer does not mean your bot has to trade it.
Your own strategy may require:
- a minimum ROI
- a minimum dollar net edge
- a maximum settlement horizon
- specific allowed venues
- minimum remaining capital
- maximum exposure per event
- maximum exposure per venue
- maximum acceptable book age
For example:
const CONFIG = { minRoi: 0.025, minNetEdge: 10, maxTradeContracts: 500, maxVenueExposureUsd: 5000, maxEventExposureUsd: 2000 }; function passesStrategy(opportunity) { if (!opportunity.executable) return false; if (opportunity.roi === null) return false; if (opportunity.netEdge === null) return false; if (opportunity.roi < CONFIG.minRoi) return false; if (opportunity.netEdge < CONFIG.minNetEdge) return false; return true; } Step 8: Keep Risk Management Separate
Strategy asks:
Is this an attractive trade?
Risk asks:
Are we allowed to take it?
Those should be separate functions.
For example:
function riskCheck({ tradeCost, currentVenueExposure, currentEventExposure }) { if (tradeCost > 1000) return false; if (currentVenueExposure + tradeCost > 5000) return false; if (currentEventExposure + tradeCost > 2000) return false; return true; } This matters even for arbitrage because capital can remain locked until settlement and venue concentration is still a real operational risk.
Step 9: Refresh the Opportunity Before Trading
Never detect an arbitrage opportunity, spend several seconds processing it and then execute using the original snapshot.
Prediction market books move.
The correct production flow is:
| Step | Action |
|---|---|
| 1 | Fetch executable arbitrage. |
| 2 | Apply strategy filters. |
| 3 | Apply portfolio and venue risk checks. |
| 4 | Fetch the arbitrage assessment again. |
| 5 | Confirm the same opportunity still qualifies at the intended size. |
| 6 | Only then enter the execution flow. |
A simplified implementation:
async function revalidate(candidate, contracts) { const latest = await fetchArbitrage({ contracts, executableOnly: true, limit: 10 }); return latest.data.find( (row) => row.clusterId === candidate.clusterId ) ?? null; } If the candidate disappears, do not trade the stale version.
The Biggest Risk: One Leg Fills and the Other Does Not
This is the part that makes "risk-free arbitrage" a dangerous phrase.
Imagine the bot sees:
Buy YES: $0.44 Buy NO: $0.52 Combined cost: $0.96 It submits the YES leg.
The YES leg fills.
Before the second order executes, the NO market jumps from 52 cents to 61 cents.
The original arbitrage has disappeared.
Your bot is now long YES without its hedge.
A production arbitrage bot needs predefined behavior for:
- partial fills
- one-leg fills
- rejected orders
- venue outages
- price movement
- timeouts
- insufficient account balances
- cancel failures
Do not wait for the first failure to decide what the bot should do.
Step 10: Check Which Execution Lanes Are Actually Available
Market-data support and hosted execution support are not the same thing.
Before trying to execute through Predictefy, query the live execution registry:
GET https://exec.predictefy.com/v1/exec/venues For example:
const response = await fetch( 'https://exec.predictefy.com/v1/exec/venues', { headers: { Authorization: `Bearer ${process.env.PREDICTEFY_API_KEY}` } } ); const lanes = await response.json(); The execution registry tells you which venue lanes are armed on the current deployment and which capabilities, such as build, submit, cancel or modify, are actually available.
If a venue is not present as an active lane, do not assume the bot can execute it through the hosted service just because its market data exists.
Predictefy's Execution Flow
For supported lanes, execution is separated into distinct stages.
| Stage | Endpoint |
|---|---|
| List live lanes | GET /v1/exec/venues |
| Build order | POST /v1/exec/{venue}/orders/build |
| Sign | Venue-specific client signing or authentication |
| Submit | POST /v1/exec/{venue}/orders/submit |
| Confirm | Read or refresh execution state |
The build route also supports dryRun: true for the documented execution flow, which is useful for testing authentication, market resolution and bounds before enabling a live trading loop.
Execution inputs differ by venue, so your bot should follow the live execution contract for the specific lane rather than hard-coding one universal order body.
Submitted Does Not Mean Filled
This is another important production detail.
An execution being acknowledged does not necessarily mean the trade is resting on the venue or has filled.
Your bot needs to confirm the resulting venue state.
The workflow should therefore be:
| Status | What Your Bot Should Assume |
|---|---|
| Built | No trade has filled. |
| Signed | No trade has filled. |
| Submitted | No fill should be assumed yet. |
| Acknowledged | The venue accepted or relayed the artifact, but fill state still needs confirmation. |
| Confirmed fill | Update positions and hedge state. |
Build a Read-Only Arbitrage Bot First
The fastest way to create an expensive bug is to combine discovery and live execution on day one.
A better rollout is:
- Build a scanner that only records opportunities.
- Store the opportunities and qualification reasons.
- Paper-trade the strategy.
- Compare expected execution against later market data.
- Add dry-run execution.
- Enable small capped live orders.
- Increase limits only after the fill and failure handling is proven.
The scanner itself can be extremely small:
async function scan() { const result = await fetchArbitrage({ contracts: 100, executableOnly: true, limit: 10 }); return result.data .filter(isTradeCandidate) .sort((a, b) => (b.roi ?? 0) - (a.roi ?? 0)); } Build the Scanning Loop
A simple polling bot might look like this:
const POLL_MS = 5000; async function run() { while (true) { try { const opportunities = await scan(); for (const opportunity of opportunities) { console.log({ market: opportunity.question, roi: opportunity.roi, netEdge: opportunity.netEdge, contracts: opportunity.contracts }); } } catch (error) { console.error(error); } await new Promise( (resolve) => setTimeout(resolve, POLL_MS) ); } } run(); The correct polling frequency depends on your API plan, rate limits, strategy and how time-sensitive your target markets are.
For a live trading system, do not treat a slow polling loop as equivalent to real-time execution infrastructure.
Store Every Opportunity, Not Just the Trades
If you only record completed trades, you lose most of the information needed to improve the bot.
Store:
- cluster ID
- market question
- both venues
- requested contracts
- YES VWAP
- NO VWAP
- fees
- net edge
- ROI
- executable status
- qualification reasons
- market timestamp
- detection timestamp
- whether you attempted execution
- whether each leg filled
- actual realized execution prices
That lets you answer useful questions later:
Which venues produce the most executable prediction market arbitrage? Which categories have the deepest spreads? How quickly do opportunities disappear? What percentage fail because of depth? What is your actual slippage relative to the assessment?
Think About Capital Efficiency, Not Just ROI
A 5% arbitrage settling in six months and a 3% arbitrage settling tomorrow are not equivalent uses of capital.
Your bot may eventually want to rank opportunities using more than raw ROI.
Useful factors include:
- net expected edge
- time until settlement
- available executable size
- venue concentration
- capital already locked
- operational risk
A simple annualized comparison can be useful for ranking, but do not treat it as a guarantee of repeatable returns. The bot still needs to consider whether capital can actually be redeployed into similar opportunities.
Is Prediction Market Arbitrage Just Polymarket vs Kalshi?
No.
Polymarket and Kalshi are a common pair to think about because the same high-profile events can appear on both platforms.
But the broader opportunity comes from fragmentation.
The same event can trade across multiple prediction markets. A bot designed only around one hard-coded pair has to be rewritten as soon as the best price appears somewhere else.
The more useful abstraction is:
Find the best executable complementary prices across supported venues.
That is why a router-level prediction market API is more flexible than a script written specifically around two exchanges.
Matched Markets vs Arbitrage: Do Not Confuse Them
This deserves repeating because it is the source of many false signals.
| Matched Markets | Arbitrage Assessment |
|---|---|
| Useful for discovery | Useful for executable qualification |
| Shows cross-venue relationships | Prices both legs at a requested size |
| Shows indicative price differences | Accounts for relevant execution gates |
| Not a buy/sell instruction | Returns explicit YES and NO buy legs |
| Does not mean profit is locked | Still requires your own risk and successful execution |
A More Complete Production Bot
Once the scanner works, the main loop might look conceptually like this:
async function processArbitrage() { const response = await fetchArbitrage({ contracts: CONFIG.contracts, executableOnly: true, limit: 10 }); for (const candidate of response.data) { if (!passesStrategy(candidate)) continue; const riskApproved = await riskEngine.check(candidate); if (!riskApproved) continue; const refreshed = await revalidate( candidate, CONFIG.contracts ); if (!refreshed) continue; if (!passesStrategy(refreshed)) continue; const executionLanes = await getExecutionLanes(); if (!canExecuteBothLegs( refreshed, executionLanes )) { continue; } await executionEngine.execute( refreshed ); await executionEngine.confirmBothLegs( refreshed ); } } The details inside execute() are where most of the operational complexity lives.
That is why the scanner and execution engine should stay separate modules.
Eight Mistakes to Avoid
1. Trading the best displayed price. Your position may be much larger than the liquidity at the first level.
2. Treating every matched-market gap as arbitrage. A price discrepancy is not automatically executable.
3. Ignoring fees. Small spreads can disappear entirely after costs.
4. Ignoring resolution differences. Two similar-looking markets can settle differently.
5. Executing from stale data. Refresh immediately before sending orders.
6. Assuming submission equals a fill. Confirm venue state after execution.
7. Ignoring the second leg. One-leg fills create directional exposure.
8. Starting with uncapped live trading. Build read-only, paper and dry-run stages first.
Frequently Asked Questions
What is a prediction market arbitrage bot?
A prediction market arbitrage bot automatically searches for complementary contracts across prediction markets where the combined executable cost may be below the settlement payout. A production bot also has to account for order-book depth, fees, resolution equivalence, stale data and execution risk rather than comparing headline prices alone.
How do you build a prediction market arbitrage bot?
Start with cross-venue market matching, qualify each candidate using live order books at your intended trade size, filter for positive net edge, apply portfolio risk limits, refresh the opportunity immediately before execution and then monitor both legs until their fill states are confirmed.
What Predictefy API endpoint finds prediction market arbitrage?
Use GET /api/router/fetchArbitrage. The contracts query parameter controls the size assessed, and executableOnly=true restricts the response to rows that earned the arbitrage label under the API's qualification rules.
What does executableOnly mean?
Setting executableOnly=true tells the arbitrage endpoint to return only rows that passed the executable qualification gates. Other cross-venue differences remain indicative price discrepancies rather than being presented as arbitrage.
Why does contract size matter for arbitrage?
Because order-book depth changes the actual execution price. An opportunity can be profitable for 50 contracts but disappear for 1,000 contracts once the bot has to walk deeper levels of the YES and NO books. Predictefy evaluates depth and fees at the requested contracts size.
What is the difference between matched markets and arbitrage?
Matched markets identify cross-venue relationships and can expose indicative price differences. Arbitrage requires additional qualification. Predictefy's arbitrage assessment uses live books and checks the relevant depth, fee, market-status, resolution and positive-net-edge conditions before assigning the arbitrage label.
Can I build a Polymarket and Kalshi arbitrage bot?
Yes, but a stronger architecture is venue-agnostic. Instead of hard-coding the bot around one pair of prediction markets, use a normalized cross-venue layer so the strategy can evaluate whichever supported venues currently offer the relevant matched prices and execution conditions.
Is prediction market arbitrage risk-free?
No. Correct payoff math does not eliminate operational risk. One leg can fill while the other fails, liquidity can disappear, prices can move, venue access can fail and contracts that appear similar can have incompatible resolution conditions. The bot should treat qualification and successful two-leg execution as separate problems.
How do I check order-book depth with Predictefy?
Use GET /api/{exchange}/fetchOrderBook with the relevant outcomeId. The normalized response exposes bids and asks with price and size so your strategy can inspect available depth. For cross-venue arbitrage, fetchArbitrage already evaluates live depth at the requested contract size.
Can Predictefy execute arbitrage trades?
Predictefy exposes hosted execution routes for lanes that are armed on the current deployment. Check GET /v1/exec/venues first. Supported flows use venue-specific build, signing or authentication, submit and status confirmation steps. Market-data support should not be assumed to mean a hosted execution lane is available.
Should I automate execution immediately?
No. A safer rollout is to build a read-only scanner first, record opportunities, paper-trade the strategy, test dry-run execution where supported and only then enable small capped live orders after failure handling has been tested.
Conclusion
Building a prediction market arbitrage bot is not mainly an arithmetic problem.
Anyone can write:
if (yesPrice + noPrice < 1) { trade(); } The difficult part is proving that yesPrice and noPrice belong to genuinely compatible markets, that both prices are available at your intended size, that the spread survives fees and that both positions can actually be obtained.
That is why the cleanest architecture separates the system into layers.
Use Predictefy to normalize cross-venue market data, discover matched markets and qualify executable prediction market arbitrage against live books.
Use your strategy engine to decide which qualified opportunities are worth taking.
Use a separate risk engine to control capital and venue exposure.
Then revalidate the opportunity and execute only through a supported lane with explicit handling for partial or failed fills.
The result is not just an arbitrage calculator.
It is a prediction market arbitrage bot built around the part that actually matters: whether the trade can be executed.
Build With Predictefy
Predictefy gives developers a unified prediction market API for matched markets, cross-venue discrepancies, live order books and size-aware executable arbitrage qualification. Use /api/router/fetchArbitrage to assess opportunities across supported venues instead of building and maintaining your own matching, order-book normalization and arbitrage infrastructure from scratch.