Cross-venue arbitrage
curl --request GET \
--url https://api.oddpool.com/arbitrage/currentimport requests
url = "https://api.oddpool.com/arbitrage/current"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.oddpool.com/arbitrage/current', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.oddpool.com/arbitrage/current",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.oddpool.com/arbitrage/current"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.oddpool.com/arbitrage/current")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.oddpool.com/arbitrage/current")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyArbitrage
Cross-venue arbitrage
Find risk-free cross-venue arbitrage opportunities, live from real-time order books.
GET
/
arbitrage
/
current
Cross-venue arbitrage
curl --request GET \
--url https://api.oddpool.com/arbitrage/currentimport requests
url = "https://api.oddpool.com/arbitrage/current"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.oddpool.com/arbitrage/current', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.oddpool.com/arbitrage/current",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.oddpool.com/arbitrage/current"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.oddpool.com/arbitrage/current")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.oddpool.com/arbitrage/current")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyFind markets where buying YES on one venue and NO on another costs less than 1 dollar combined. The gap is risk-free profit. Prices and order books are live — recomputed continuously from real-time venue WebSocket feeds (Kalshi, Polymarket, Opinion), not a periodic snapshot.
Each venue block includes execution-ready identifiers you can pass directly to the venue’s trading API:
Example: “Will the Fed cut rates?” — YES is 32c on Kalshi, NO is 60c on Polymarket. Buy both for 92c, locking in an 8c gross spread. After ~3c in venue fees (Kalshi taker + Polymarket taker), you pocket about 5c per contract guaranteed.
Parameters
float
default:"0"
Minimum net profit after fees, in cents.
boolean
default:"false"
When
true, attaches each leg’s live ask ladder (top 25 levels) under orderbook, so you can size an order without a second call.integer
deprecated
Deprecated and ignored. The legacy endpoint used it as a staleness window; the live cache is always current, so it has no effect. Accepted only for backwards compatibility.
Example
curl -H "X-API-Key: your_api_key" \
"https://api.oddpool.com/arbitrage/current?min_net_cents=0.5&orderbook=true"
import requests
response = requests.get(
"https://api.oddpool.com/arbitrage/current",
headers={"X-API-Key": "your_api_key"},
params={"min_net_cents": 0.5, "orderbook": True},
)
opportunities = response.json()
Response
A JSON array of opportunities, sorted bynet_cents (highest first). An empty array means there are no current opportunities at or above your threshold. net_cents is your per-contract profit after all fees.
The opportunity tells you the trade directly: buy YES on buy_yes_market, buy NO on buy_no_market. yes_leg / no_leg give the exact (market, identifier, book_side) to hit for each side.
gross_cents / fee_cents / net_cents are the top-of-book economics (per contract): net_cents = round((1 − (best_yes_ask + best_no_ask)) × 100) − fees. executable_size and max_profit_dollars are the depth-aware answer to “how much can I actually take, and what’s it worth?” — they walk both legs’ ask ladders together: at each price level they fill the smaller of the two available sizes, add (level_net_after_fees / 100) × size to the profit, and advance until a level pair is no longer profitable after fees (or the ladders run out). So executable_size is the total fillable contracts and max_profit_dollars the summed profit across all profitable levels.
max_profit_dollars is a conservative floor, not a guarantee. It only considers the top 25 levels, so true fillable depth beyond that isn’t counted. Fees are applied per level using the same per-venue rates as net_cents (Kalshi sector taker, Polymarket per-market feeSchedule.rate, Opinion topic rate + floor). It does not model slippage from latency, partial fills, or order-placement minimums — size against executable_size and the attached orderbook, not blindly.- Kalshi —
market_tickeris the ticker accepted by Kalshi’s trade API. - Polymarket —
condition_idis the on-chain condition;yes_token_id/no_token_idare the CLOB token IDs for placing orders. - Opinion —
child_market_idis the categorical child market ID;yes_token_id/no_token_idare the trading-side token IDs.
Prices and order books are real-time; volume and liquidity refresh roughly hourly. Opinion does not expose 24h volume or liquidity, so
volume_24h and liquidity are null for Opinion legs. timestamp is when the opportunity was last recomputed.[
{
"event_id": "stanley-cup-2026",
"event_title": "NHL Stanley Cup Champion 2026",
"outcome_key": "colorado_avalanche",
"label": "Colorado Avalanche",
"timestamp": "2026-05-26T02:21:54.190359",
"market_type": null,
"resolution_time": "2026-06-29T19:00:00",
"kalshi_event_ticker": "KXNHL-26",
"polymarket_event_slug": "2026-nhl-stanley-cup-champion",
"opinion_market_id": 345,
"kalshi": {
"market_ticker": "KXNHL-26-COL",
"yes_ask": 0.07, "no_ask": 0.95,
"volume": 6352005, "volume_24h": 320871,
"open_interest": 3146772
},
"polymarket": {
"condition_id": "0xf8f63bb47b2a7c2e0c1be3cedf4075079b11c07476d76a9469065b0c4791961a",
"yes_token_id": "101738487887518832481587379955535423775326921556438741919099866785354159699479",
"no_token_id": "87978082071653935678874296685430503892266481242311708420787197372467948088235",
"yes_ask": 0.055, "no_ask": 0.948,
"volume": 15219511.07, "volume_24h": 215850.28,
"liquidity": 50818.82
},
"opinion": {
"child_market_id": 5566,
"yes_token_id": "109494079162951671873858278805080808339824558282001815297323400464995130701684",
"no_token_id": "74166100071861466695478367383768425561540521028754831523233794115776851950059",
"yes_ask": 0.21, "no_ask": 0.782,
"volume": 38765.84, "volume_24h": null, "liquidity": null
},
"buy_yes_market": "polymarket",
"buy_no_market": "opinion",
"gross_cents": 16.3,
"fee_cents": 2,
"net_cents": 14.3,
"executable_size": 97.87,
"max_profit_dollars": 13.94,
"yes_leg": { "market": "polymarket", "identifier": "101738487887518832481587379955535423775326921556438741919099866785354159699479", "book_side": "ask" },
"no_leg": { "market": "opinion", "identifier": "74166100071861466695478367383768425561540521028754831523233794115776851950059", "book_side": "ask" },
"orderbook": {
"as_of": "2026-05-26T02:21:47.062741",
"yes": {
"market": "polymarket",
"asks": [
{ "price": 0.055, "size": 42.97 },
{ "price": 0.056, "size": 14989.0 }
]
},
"no": {
"market": "opinion",
"asks": [
{ "price": 0.782, "size": 97.87 }
]
}
}
}
]
orderbook is only present when you pass orderbook=true. It carries the top 25 ask levels per leg — ask side only (the price ladder to buy that outcome).