OpticOdds for Prediction Market Makers
Prediction Market Makers
What you'll learn: How prediction market makers can use the OpticOdds API to aggregate exchange data, consume order book depth, map to native exchange identifiers, stream real-time price movements, and build automated market-making strategies across Kalshi, Polymarket, Betfair Exchange, and other prediction market platforms.
Why OpticOdds for Prediction Markets?
Prediction market making requires speed, breadth, and precision. You need real-time prices from multiple exchanges, order book depth to gauge liquidity, and native exchange identifiers to route orders back to the source. Building and maintaining direct integrations with every exchange is expensive and fragile — each has its own API, data format, and rate limits.
OpticOdds solves this by normalizing data from every major prediction market exchange into a single API. You get one integration that gives you:
- Aggregated exchange prices — Real-time odds from Kalshi, Polymarket, Betfair Exchange, SX Bet, Sporttrade, Novig, BetDEX, and more
- Order book depth — Bid/ask levels with size at each price point via the
order_bookfield - Native exchange IDs — The
source_idsfield maps every selection back to the exchange's own market and selection identifiers, so you can route orders directly - Streaming updates — SSE-powered real-time feeds for odds changes and results
- Full market coverage — Sports markets across all major exchanges, with politics, entertainment, and additional categories coming soon
- Limits data — Max stake information to understand available liquidity
- Historical prices — Full tick-level price history for backtesting strategies
Supported Prediction Market Exchanges
OpticOdds tracks the following prediction market and exchange-style sportsbooks:
| Exchange | Sportsbook ID | Type |
|---|---|---|
| Kalshi | kalshi | CFTC-regulated event contracts |
| Polymarket | polymarket | Crypto-based prediction market |
| Polymarket (USA) | polymarket_usa | US-regulated Polymarket |
| Betfair Exchange | betfair_exchange | Traditional betting exchange |
| Betfair Exchange (Lay) | betfair_exchange_lay | Lay-side Betfair prices |
| Betfair Exchange (Australia) | betfair_exchange_au | Australian Betfair market |
| SX Bet | sx_bet | Crypto sports exchange |
| Sporttrade | sporttrade | US-regulated sports exchange |
| Novig | novig | Vig-free exchange model |
| BetDEX | betdex | Solana-based exchange |
Tip: Use the
sportsbookparameter on any odds endpoint to filter to just the exchanges you care about. You can pass up to 5 per request.
Key Concepts for Market Makers
The Odds Model — What You Get Back
Every odds response from OpticOdds returns an array of Odd objects. For prediction market makers, the critical fields are:
{
"id": "40294-35775-2025-11-30:kalshi:moneyline:new_york_knicks",
"sportsbook": "Kalshi",
"market": "Moneyline",
"name": "New York Knicks",
"is_main": true,
"selection": "New York Knicks",
"normalized_selection": "new_york_knicks",
"market_id": "moneyline",
"selection_line": null,
"price": 1.31,
"timestamp": 1764536802.4131637,
"limits": {
"max": 108.25
},
"order_book": [
[1.31, 108.25],
[1.293, 91866.72],
[1.278, 25744.13]
],
"source_ids": {
"market_id": "KXNBAGAME-25NOV30TORNYK-NYK",
"selection_id": "yes"
}
}Let's break down the fields that matter most for market making:
order_book — Exchange Order Book Depth
order_book — Exchange Order Book DepthThe order_book field exposes the full depth of book from the exchange. It's an array of [price, size] tuples representing available liquidity at each price level.
"order_book": [
[1.31, 108.25], // Best price: 1.31 decimal odds, $108.25 available
[1.293, 91866.72], // Next level: 1.293 decimal odds, $91,866.72 available
[1.278, 25744.13] // Third level: 1.278 decimal odds, $25,744.13 available
]How to read it:
- First element = price in decimal odds format
- Second element = size (amount available at that price, in the exchange's base currency)
- First tuple = best available price (top of book)
- Subsequent tuples = deeper levels of the book, progressively worse prices with potentially larger size
Why this matters for market makers:
- Spread calculation — Compare the best bid and best ask across exchanges to identify the true market spread
- Liquidity assessment — Size at each level tells you how much you can execute before moving the price
- Depth analysis — Thin books present both opportunity (wider spreads) and risk (slippage)
- Cross-exchange arbitrage — When the best ask on one exchange is below the best bid on another, there's an arbitrage opportunity
Available on: Both
/fixtures/odds(snapshot) and/stream/odds(real-time streaming)
source_ids — Native Exchange Identifiers
source_ids — Native Exchange IdentifiersThe source_ids field maps each OpticOdds selection back to the exchange's own identifiers. This is essential for order routing — once you identify an opportunity via OpticOdds, you need to place the order on the exchange using its native API.
"source_ids": {
"market_id": "KXNBAGAME-25NOV30TORNYK-NYK",
"selection_id": "yes"
}Exchange-specific mappings:
| Exchange | market_id example | selection_id example |
|---|---|---|
| Kalshi | KXNBAGAME-25NOV30TORNYK-NYK | yes / no |
| Polymarket | CLOB token ID | Outcome token ID |
| Betfair | Market ID | Selection ID / Runner ID |
Why this matters for market makers:
- Direct order placement — Use the
market_idandselection_idto construct orders on the exchange's native API without manual mapping - Symbol resolution — No need to maintain your own mapping tables between OpticOdds fixture IDs and exchange-specific tickers
- Multi-exchange strategies — Build unified strategies that discover opportunities via OpticOdds and execute on the native exchange
limits — Available Liquidity
limits — Available LiquidityThe limits field shows the maximum stake the exchange will accept at the current best price.
"limits": {
"max": 108.25
}This is the top-of-book size — the most you can trade at the displayed price before hitting the next level. For market makers, this is critical for position sizing and execution planning.
price — The Best Available Price
price — The Best Available PriceThe price field always reflects the best available price (top of book) in decimal odds format. This is the first entry in the order_book array.
Odds format flexibility: By default, prices come in decimal format. You can request other formats using the
odds_formatparameter:AMERICAN,DECIMAL,PROBABILITY,MALAY,HONG_KONG, orINDONESIAN. For quantitative work,PROBABILITYis often most useful.
exclude_fees — Raw Exchange Prices
exclude_fees — Raw Exchange PricesPrediction market exchanges charge fees (trading fees, settlement fees, etc.) that affect effective pricing. By default, OpticOdds includes fee adjustments in the returned odds. To get the raw, pre-fee exchange prices, use:
?exclude_fees=true
This is important for market makers who need to calculate their own fee models or who have negotiated custom fee tiers with exchanges.
Step-by-Step Integration
Step 1: Authentication
All API requests require your OpticOdds API key.
Header authentication (recommended):
curl --location 'https://api.opticodds.com/api/v3/fixtures/odds' \
--header 'X-Api-Key: YOUR_API_KEY'Query parameter authentication:
curl 'https://api.opticodds.com/api/v3/fixtures/odds?key=YOUR_API_KEY'Base URL:
https://api.opticodds.com/api/v3
Rate Limits
| Tier | Limit |
|---|---|
| Standard endpoints | 2,500 requests / 15 seconds |
| Streaming endpoints | 250 requests / 15 seconds |
| Historical endpoints | 10 requests / 15 seconds |
Step 2: Discover Available Markets
Start by finding what prediction market content is available. The discovery endpoints let you explore available sports, leagues, and markets.
Get all sports (including prediction market categories):
GET /api/v3/sportsOpticOdds currently supports sports markets on prediction market exchanges — the same sports you'd find on traditional sportsbooks, but traded on exchange order books. Support for additional categories is coming soon:
| Category | Status | Examples |
|---|---|---|
| Sports | Available now | NBA, NFL, MLB, NHL, soccer, tennis, MMA, and more |
| Politics | Coming soon | Presidential elections, state races, congressional outcomes |
| Entertainment | Coming soon | Academy Awards, reality TV, cultural events |
| Economics / Weather / Crypto | Coming soon | Fed rate decisions, temperature markets, token prices |
Get leagues for a sport:
GET /api/v3/leagues?sport=basketballGet available markets:
GET /api/v3/markets?sport=basketballExchanges list standard market types — moneyline, spreads, totals, and player props — alongside traditional sportsbooks, giving you a unified view across both exchange and fixed-odds pricing.
Step 3: Find Active Fixtures
Pull the current fixtures (events) that prediction markets are trading:
GET /api/v3/fixtures/active?sport=basketball&league=NBAFilter by exchange:
GET /api/v3/fixtures/active?sport=basketball&sportsbook=kalshi,polymarketResponse includes:
{
"data": [
{
"id": "fixture-id-here",
"sport": "Politics",
"league": "US Politics",
"start_date": "2026-11-03T00:00:00Z",
"home_team": "Candidate A",
"away_team": "Candidate B",
"status": "unplayed",
"is_live": false
}
]
}Fixture statuses:
unplayed→live→completed/cancelled
Step 4: Pull Odds with Order Book Data
This is the core endpoint for market makers. Request odds for specific fixtures and sportsbooks, and get back the full order book:
GET /api/v3/fixtures/odds?fixture_id=FIXTURE_ID&sportsbook=kalshi,polymarket,betfair_exchange&odds_format=DECIMALPython example — Pull and compare exchange prices:
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.opticodds.com/api/v3"
# Prediction market exchanges to monitor
EXCHANGES = ["kalshi", "polymarket", "betfair_exchange", "sporttrade", "sx_bet"]
def get_exchange_odds(fixture_id):
"""Pull odds from all prediction market exchanges for a fixture."""
response = requests.get(
f"{BASE_URL}/fixtures/odds",
headers={"X-Api-Key": API_KEY},
params={
"fixture_id": fixture_id,
"sportsbook": ",".join(EXCHANGES),
"odds_format": "DECIMAL",
"exclude_fees": "true" # Raw exchange prices
}
)
return response.json()["data"]
def analyze_order_books(odds_data):
"""Compare order books across exchanges for arbitrage opportunities."""
# Group by selection
selections = {}
for fixture in odds_data:
for odd in fixture.get("odds", []):
selection = odd["normalized_selection"]
if selection not in selections:
selections[selection] = []
selections[selection].append({
"exchange": odd["sportsbook"],
"price": odd["price"],
"order_book": odd.get("order_book", []),
"limits": odd.get("limits", {}),
"source_ids": odd.get("source_ids", {})
})
# Find best prices across exchanges
for selection, exchange_odds in selections.items():
best_back = max(exchange_odds, key=lambda x: x["price"])
print(f"\n--- {selection} ---")
for eo in sorted(exchange_odds, key=lambda x: -x["price"]):
book_depth = len(eo["order_book"])
top_size = eo["order_book"][0][1] if eo["order_book"] else 0
print(f" {eo['exchange']:20s} | Price: {eo['price']:.3f} | "
f"Top Size: ${top_size:,.2f} | Depth: {book_depth} levels")
if eo.get("source_ids"):
print(f" {'':20s} | Exchange ID: {eo['source_ids'].get('market_id', 'N/A')}")
# Usage
odds = get_exchange_odds("your-fixture-id")
analyze_order_books(odds)Example output:
--- candidate_a ---
Kalshi | Price: 1.667 | Top Size: $50,000.00 | Depth: 5 levels
| Exchange ID: KXPRESWIN-26-DEM
Polymarket | Price: 1.650 | Top Size: $125,000.00 | Depth: 8 levels
| Exchange ID: 0x1234...abcd
Betfair Exchange | Price: 1.640 | Top Size: £30,000.00 | Depth: 6 levels
| Exchange ID: 1.23456789
--- candidate_b ---
Kalshi | Price: 2.500 | Top Size: $35,000.00 | Depth: 4 levels
Polymarket | Price: 2.550 | Top Size: $80,000.00 | Depth: 7 levels
Betfair Exchange | Price: 2.600 | Top Size: £22,000.00 | Depth: 5 levels
Step 5: Stream Real-Time Updates
For market making, polling is too slow. Use the SSE streaming endpoint to get real-time price and order book updates:
GET /api/v3/stream/odds?fixture_id=FIXTURE_ID&sportsbook=kalshi,polymarket&odds_format=DECIMALPython streaming example:
import requests
import json
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.opticodds.com/api/v3"
def stream_exchange_odds(fixture_id, exchanges):
"""Stream real-time odds updates from prediction market exchanges."""
response = requests.get(
f"{BASE_URL}/stream/odds",
headers={"X-Api-Key": API_KEY},
params={
"fixture_id": fixture_id,
"sportsbook": ",".join(exchanges),
"odds_format": "DECIMAL",
"exclude_fees": "true"
},
stream=True
)
for line in response.iter_lines():
if line:
decoded = line.decode("utf-8")
if decoded.startswith("data:"):
payload = json.loads(decoded[5:].strip())
process_update(payload)
def process_update(update):
"""Process a real-time odds update."""
for odd in update.get("odds", []):
exchange = odd["sportsbook"]
selection = odd["selection"]
price = odd["price"]
order_book = odd.get("order_book", [])
source_ids = odd.get("source_ids", {})
# Calculate mid-market from order book
if len(order_book) >= 1:
top_price = order_book[0][0]
top_size = order_book[0][1]
print(f"[{exchange}] {selection}: {price:.3f} "
f"(top: {top_price:.3f} x ${top_size:,.2f}) "
f"| exchange_id: {source_ids.get('market_id', 'N/A')}")
# Stream from multiple exchanges
stream_exchange_odds("fixture-id", ["kalshi", "polymarket", "betfair_exchange"])Node.js streaming example:
const https = require('https');
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.opticodds.com/api/v3';
function streamExchangeOdds(fixtureId, exchanges) {
const params = new URLSearchParams({
fixture_id: fixtureId,
sportsbook: exchanges.join(','),
odds_format: 'DECIMAL',
exclude_fees: 'true'
});
const url = `${BASE_URL}/stream/odds?${params}`;
https.get(url, { headers: { 'X-Api-Key': API_KEY } }, (res) => {
let buffer = '';
res.on('data', (chunk) => {
buffer += chunk.toString();
const lines = buffer.split('\n');
buffer = lines.pop(); // Keep incomplete line in buffer
for (const line of lines) {
if (line.startsWith('data:')) {
const payload = JSON.parse(line.slice(5).trim());
processUpdate(payload);
}
}
});
res.on('error', (err) => {
console.error('Stream error:', err.message);
// Implement reconnection logic here
});
});
}
function processUpdate(update) {
for (const odd of update.odds || []) {
const book = odd.order_book || [];
const topLevel = book[0] || [0, 0];
console.log(
`[${odd.sportsbook}] ${odd.selection}: ${odd.price.toFixed(3)} ` +
`(${topLevel[0].toFixed(3)} x $${topLevel[1].toLocaleString()}) ` +
`| ${odd.source_ids?.market_id || 'N/A'}`
);
}
}
streamExchangeOdds('fixture-id', ['kalshi', 'polymarket', 'betfair_exchange']);Reconnection: SSE streams may disconnect. Implement automatic reconnection with exponential backoff. The streaming rate limit is 250 requests per 15 seconds.
Step 6: Monitor Results and Settlement
Track event outcomes for position management and P&L calculation:
Stream live results:
GET /api/v3/stream/results?sport=basketballGrade settled bets:
GET /api/v3/fixtures/grade?fixture_id=FIXTURE_ID&sportsbook=kalshi&market=moneyline&selection=candidate_aGrading outcomes:
| Grade | Meaning |
|---|---|
Won | Selection won — full payout |
Lost | Selection lost — no payout |
Refunded | Event cancelled or voided — stake returned |
Half Won | Partial win (Asian handicap/totals) |
Half Lost | Partial loss (Asian handicap/totals) |
Pending | Event not yet settled |
Step 7: Backtest with Historical Data
Use historical odds to backtest market-making strategies:
GET /api/v3/fixtures/odds/history?fixture_id=FIXTURE_ID&sportsbook=kalshi&market=moneylineThis returns every price movement for the given fixture, sportsbook, and market — giving you the full tick history for backtesting.
Rate limit: Historical endpoints are limited to 10 requests per 15 seconds. Plan your data collection accordingly.
Market-Making Strategy Patterns
Pattern 1: Cross-Exchange Arbitrage
The most straightforward strategy — identify when the same event is priced differently across exchanges.
def find_arbitrage(selections_data):
"""
Find cross-exchange arbitrage opportunities.
An arb exists when you can buy YES on one exchange and NO on another
for a combined cost less than $1.00 (in probability terms).
"""
opportunities = []
for selection, exchanges in selections_data.items():
# Convert to implied probability
for ex in exchanges:
ex["implied_prob"] = 1 / ex["price"]
# Best price to back this selection (highest odds = lowest implied prob)
best_back = min(exchanges, key=lambda x: x["implied_prob"])
# For a binary market: the opposite side's best price
# You'd compare against the complementary selection
# If prob_yes + prob_no < 1.0 across exchanges, there's an arb
opportunities.append({
"selection": selection,
"best_exchange": best_back["exchange"],
"best_price": best_back["price"],
"implied_prob": best_back["implied_prob"],
"source_ids": best_back["source_ids"],
"available_size": best_back["order_book"][0][1] if best_back["order_book"] else 0
})
return opportunitiesPattern 2: Liquidity Provision
Provide liquidity by quoting both sides of a market, profiting from the spread.
def calculate_fair_value(order_books_by_exchange):
"""
Calculate a volume-weighted fair value from multiple exchange order books.
Use this as your mid-market reference for quoting.
"""
total_weighted_price = 0
total_size = 0
for exchange, book in order_books_by_exchange.items():
for price, size in book:
total_weighted_price += price * size
total_size += size
if total_size == 0:
return None
return total_weighted_price / total_size
def generate_quotes(fair_value, half_spread, max_size):
"""
Generate bid/ask quotes around a fair value.
"""
return {
"bid": {
"price": fair_value - half_spread,
"size": max_size
},
"ask": {
"price": fair_value + half_spread,
"size": max_size
}
}Pattern 3: Event-Driven Market Making
Widen spreads or pull quotes during high-volatility events (game start, key moments, breaking news).
def adjust_spread_for_volatility(base_spread, is_live, recent_price_moves):
"""
Dynamically adjust spread width based on market conditions.
"""
spread = base_spread
# Widen during live events
if is_live:
spread *= 2.0
# Widen if recent price moves are large
if recent_price_moves:
avg_move = sum(abs(m) for m in recent_price_moves) / len(recent_price_moves)
if avg_move > 0.05: # >5% moves
spread *= 1.5
return spreadPricing Combo Markets with the SGP Pricer
Prediction market exchanges are increasingly offering combo bets — multi-leg wagers within the same event (the exchange equivalent of same game parlays). For market makers, this opens up a new surface area: pricing, quoting, and arbitraging correlated multi-outcome markets.
OpticOdds provides a dedicated SGP (Same Game Parlay) Pricer that handles the hard part — correlation-adjusted pricing across multiple legs. Instead of naively multiplying independent probabilities (which dramatically misprices correlated outcomes), the engine models the relationships between selections and returns a fair combined price.
Why This Matters for Prediction Market Makers
- Price combo markets accurately — If an exchange offers a "Player A Over 25.5 Points + Team Win" combo, you need to know the correlated fair value. Multiplying the individual leg prices will underprice or overprice the combo depending on the direction of correlation.
- Identify mispriced combos — Compare the exchange's posted combo price against the SGP Pricer's fair value to find edge.
- Quote your own combos — If you're providing liquidity on combo markets, use the pricer to generate your bid/ask around the correlated fair value.
- Cross-exchange combo arbitrage — Different exchanges may price the same multi-leg combination differently. Use the pricer as your reference to spot discrepancies.
How It Works
The SGP Pricer is available via two paths:
Parlay API (POST /parlay/odds) — You choose the pricing source (specific sportsbooks or the OpticOdds AI consensus line) and submit your legs. Best for custom trading desks.
curl -X POST "https://api.opticodds.com/api/v3/parlay/odds" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sportsbooks": ["OpticOdds AI"],
"entries": [
{
"market": "Player Points",
"name": "Jayson Tatum Over 25.5",
"fixture_id": "FIXTURE_ID",
"price_decimal": 1.87
},
{
"market": "Moneyline",
"name": "Boston Celtics",
"fixture_id": "FIXTURE_ID",
"price_decimal": 1.45
}
]
}'Copilot Bet Builder (POST /copilot/parlay/odds) — Uses your pre-configured Copilot pricing rules. Best for operators running a book.
Coverage
The SGP engine supports:
- 3,000+ unique markets across global leagues
- 1,500+ unique markets per event spanning 30+ leagues
- Sports: Baseball, Basketball, American Football, Golf, Hockey, MMA, Soccer, Tennis
- Processing speed: 1M+ odds per second
- Pre-match and in-play pricing
Using SGP Pricing in Your Market-Making Workflow
import requests
API_KEY = "YOUR_API_KEY"
def get_sgp_fair_value(fixture_id, legs):
"""
Get correlation-adjusted combo pricing from the SGP Pricer.
Use as your fair value reference when quoting combo markets.
"""
entries = []
for leg in legs:
entries.append({
"market": leg["market"],
"name": leg["name"],
"fixture_id": fixture_id,
"price_decimal": leg["price"] # Your observed single-leg price
})
response = requests.post(
"https://api.opticodds.com/api/v3/parlay/odds",
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
json={
"sportsbooks": ["OpticOdds AI"],
"entries": entries
}
)
result = response.json()["data"]["OpticOdds AI"]
return result["price"] # Correlated combo price
def find_combo_edge(exchange_combo_price, sgp_fair_value):
"""
Compare an exchange's combo price to the SGP Pricer's fair value.
Positive edge = exchange is offering better than fair value.
"""
# Convert American odds to implied probability
def to_prob(american):
if american > 0:
return 100 / (american + 100)
else:
return abs(american) / (abs(american) + 100)
exchange_prob = to_prob(exchange_combo_price)
fair_prob = to_prob(sgp_fair_value)
edge = fair_prob - exchange_prob # Positive = exchange is underpriced
return {
"exchange_implied": round(exchange_prob, 4),
"fair_implied": round(fair_prob, 4),
"edge": round(edge, 4)
}Full Guide: For complete SGP Pricer documentation including request schemas, response formats, the OpticOdds AI consensus line, and Copilot Bet Builder setup, see the SGP Pricer & Bet Builder Guide. Contact your OpticOdds account team to get set up with the SGP Pricer.
Working with Specific Exchanges
Kalshi
Kalshi is a CFTC-regulated event contracts exchange. Events are structured as binary yes/no contracts that settle at $1.00 or $0.00.
- Sportsbook ID:
kalshi - Source IDs format:
market_id= Kalshi event ticker (e.g.,KXNBAGAME-25NOV30TORNYK-NYK),selection_id=yesorno - Categories: Sports (politics, economics, weather, entertainment, and more coming soon)
- Key feature: Deep liquidity on sports event contracts
Polymarket
Polymarket is a crypto-based prediction market using CLOB (Central Limit Order Book) on Polygon.
- Sportsbook ID:
polymarket/polymarket_usa - Source IDs format:
market_id= condition ID,selection_id= outcome token - Categories: Sports (politics, crypto, culture, and more coming soon)
- Key feature: Very high liquidity; 24/7 trading
Betfair Exchange
The world's largest betting exchange with deep liquidity across sports.
- Sportsbook IDs:
betfair_exchange(back),betfair_exchange_lay(lay),betfair_exchange_au(Australia) - Source IDs format:
market_id= Betfair market ID,selection_id= runner ID - Categories: Sports markets with deep global coverage
- Key feature: Deepest sports liquidity; back AND lay prices available separately
Betfair Lay prices: The
betfair_exchange_laysportsbook gives you the lay side. Combining back and lay books gives you the full two-sided order book.
SX Bet, Sporttrade, BetDEX, Novig
These exchanges each provide unique angles:
- SX Bet (
sx_bet) — Crypto sports exchange with peer-to-peer order matching - Sporttrade (
sporttrade) — US-regulated exchange with spread and total markets - BetDEX (
betdex) — Solana-based decentralized exchange - Novig (
novig) — Zero-vig exchange model where users trade against each other
Complete Endpoint Reference for Market Makers
| Endpoint | Method | Purpose |
|---|---|---|
/sports | GET | List all available sports |
/leagues | GET | List leagues within a sport |
/sportsbooks | GET | List all supported sportsbooks/exchanges |
/markets | GET | List available market types for a sport |
/fixtures/active | GET | Get upcoming and live events |
/fixtures/odds | GET | Core — Pull odds snapshot with order book and source IDs |
/fixtures/odds/history | GET | Historical price movements for backtesting |
/fixtures/grade | GET | Bet settlement / grading |
/fixtures/results | GET | Event results and scores |
/stream/odds | GET (SSE) | Core — Real-time odds streaming with order book updates |
/stream/results | GET (SSE) | Real-time results and score streaming |
/parlay/odds | POST | SGP Pricer — correlation-adjusted combo/parlay pricing |
/copilot/parlay/odds | POST | Copilot Bet Builder — config-driven combo pricing |
Best Practices for Prediction Market Makers
Data Architecture
- Stream, don't poll — Use
/stream/oddsas your primary data source. Polling/fixtures/oddsintroduces latency and wastes rate limit budget. - Snapshot on startup — When your system starts, pull a full snapshot via
/fixtures/oddsto hydrate your state, then switch to streaming for updates. - Use
exclude_fees=true— Always request raw prices and apply your own fee model. Exchange fee tiers vary by volume and may be negotiated. - Request
DECIMALorPROBABILITYformat — These are the most useful for quantitative calculations. American odds require conversion before any math.
Execution
- Use
source_idsfor order routing — When you identify an opportunity, use themarket_idandselection_idfromsource_idsto construct your order on the native exchange API. - Respect
limits— Themaxfield inlimitstells you the most you can trade at the current best price. Size your orders accordingly. - Check
order_bookdepth before trading — Top-of-book size may be small. Check deeper levels to understand the full available liquidity and expected slippage. - Handle Betfair back/lay separately — Query both
betfair_exchangeandbetfair_exchange_layto reconstruct the full two-sided book.
Risk Management
- Monitor results streams — Use
/stream/resultsto track live scores and event status changes for real-time position management. - Use the grading endpoint — Don't rely on your own settlement logic. Use
/fixtures/gradeto confirm official outcomes. - Track multiple exchanges — Price discrepancies across exchanges can signal information asymmetry. A sudden move on one exchange often predicts moves on others.
- Build reconnection logic — SSE streams will occasionally disconnect. Implement automatic reconnection with exponential backoff, and re-snapshot on reconnect to avoid missing updates.
Inventory & Position Management
- Track net exposure per event — Aggregate your positions across exchanges into a single view. Use
normalized_selectionto match the same outcome across different books. - Use
fixture_idas your canonical event key — OpticOdds normalizes the same real-world event into a single fixture ID even when it appears on multiple exchanges. Build your position book around this ID. - Automate settlement reconciliation — After an event completes, call
/fixtures/gradefor each exchange you traded on and reconcile against your expected P&L. Flag any discrepancies immediately.
Latency & Infrastructure
- Co-locate your consumer — Place your SSE stream consumer as close to OpticOdds infrastructure as possible to minimize network latency on price updates.
- Decouple ingestion from strategy — Write stream data to a local queue or in-memory store, and have your pricing/execution engine read from that store. This prevents slow strategy logic from backing up your data feed.
- Maintain a local order book mirror — Rather than parsing the full
order_bookarray on every update, maintain a local copy and apply deltas. This reduces processing time on hot paths. - Timestamp everything — The
timestampfield on each odd tells you when the exchange last updated. Use this to detect stale quotes and avoid trading on outdated prices.
Multi-Market Strategies
- Cross-sport correlation trading — Some markets are correlated across sports (e.g., NBA and NCAAB lines moving together, or a major injury affecting multiple prop markets). Use OpticOdds' multi-sport coverage to monitor related markets simultaneously.
- Combine traditional sportsbooks with exchanges — OpticOdds covers 170+ traditional sportsbooks alongside exchanges. Use sharp book prices (Pinnacle, Circa, etc.) as fair value references when making markets on exchanges.
- Monitor
is_livetransitions — When a fixture flips fromunplayedtolive, exchange order books often thin out and spreads widen. This is both a risk and an opportunity for market makers. - Use historical data for regime detection — Pull
/fixtures/odds/historyto study how order book depth and spreads behave around similar events. Build models that adapt your quoting strategy based on historical patterns.
Rate Limits
- Budget your rate limits carefully:
- Streaming: 250 connections / 15 seconds — plan your stream topology
- Standard: 2,500 / 15 seconds — plenty for snapshots and discovery
- Historical: 10 / 15 seconds — batch your backtest data pulls
Quick Start Checklist
- Get your OpticOdds API key
- Test authentication with a simple
/sportscall - Discover available sports and leagues traded on prediction market exchanges
- Pull odds for a fixture with
exclude_fees=trueand inspect theorder_bookandsource_idsfields - Set up SSE streaming for real-time order book updates
- Map
source_idsto your exchange API integrations for order routing - Implement reconnection logic for SSE streams
- Build your pricing model using aggregated order book data
- Backtest with historical data via
/fixtures/odds/history - Explore the SGP Pricer for pricing combo/multi-leg markets
- Go live with position management using
/stream/resultsand/fixtures/grade
Glossary
| Term | Definition |
|---|---|
| CLOB | Central Limit Order Book — the order matching model used by exchanges like Kalshi and Polymarket |
| Order Book | The stack of resting orders at various price/size levels on an exchange |
| Top of Book | The best available price and its size — the first entry in order_book |
| Depth | The number of price levels in the order book; deeper books mean more liquidity |
| Spread | The difference between the best bid and best ask prices |
| Implied Probability | 1 / decimal_odds — the market's estimated probability of an outcome |
| Source IDs | Native exchange identifiers (market_id, selection_id) for direct API interaction |
| SSE | Server-Sent Events — the streaming protocol used by OpticOdds for real-time data |
| Lay | A bet against an outcome (used on Betfair Exchange) — the other side of a back bet |
| Settlement | The process of determining the outcome and paying out winning positions |
| Vig / Juice | The fee or margin built into odds; exclude_fees=true removes this |
| Tick | A single price movement; historical data provides every tick |
| Binary Contract | An event contract that settles at $1.00 (yes) or $0.00 (no) |
Questions? Contact your OpticOdds account team for help with integration or custom data requirements.
Updated about 4 hours ago