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_book field
  • Native exchange IDs — The source_ids field 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:

ExchangeSportsbook IDType
KalshikalshiCFTC-regulated event contracts
PolymarketpolymarketCrypto-based prediction market
Polymarket (USA)polymarket_usaUS-regulated Polymarket
Betfair Exchangebetfair_exchangeTraditional betting exchange
Betfair Exchange (Lay)betfair_exchange_layLay-side Betfair prices
Betfair Exchange (Australia)betfair_exchange_auAustralian Betfair market
SX Betsx_betCrypto sports exchange
SporttradesporttradeUS-regulated sports exchange
NovignovigVig-free exchange model
BetDEXbetdexSolana-based exchange

Tip: Use the sportsbook parameter 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

The 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

The 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:

Exchangemarket_id exampleselection_id example
KalshiKXNBAGAME-25NOV30TORNYK-NYKyes / no
PolymarketCLOB token IDOutcome token ID
BetfairMarket IDSelection ID / Runner ID

Why this matters for market makers:

  • Direct order placement — Use the market_id and selection_id to 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

The 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

The 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_format parameter: AMERICAN, DECIMAL, PROBABILITY, MALAY, HONG_KONG, or INDONESIAN. For quantitative work, PROBABILITY is often most useful.


exclude_fees — Raw Exchange Prices

Prediction 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

TierLimit
Standard endpoints2,500 requests / 15 seconds
Streaming endpoints250 requests / 15 seconds
Historical endpoints10 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/sports

OpticOdds 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:

CategoryStatusExamples
SportsAvailable nowNBA, NFL, MLB, NHL, soccer, tennis, MMA, and more
PoliticsComing soonPresidential elections, state races, congressional outcomes
EntertainmentComing soonAcademy Awards, reality TV, cultural events
Economics / Weather / CryptoComing soonFed rate decisions, temperature markets, token prices

Get leagues for a sport:

GET /api/v3/leagues?sport=basketball

Get available markets:

GET /api/v3/markets?sport=basketball

Exchanges 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=NBA

Filter by exchange:

GET /api/v3/fixtures/active?sport=basketball&sportsbook=kalshi,polymarket

Response 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: unplayedlivecompleted / 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=DECIMAL

Python 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=DECIMAL

Python 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=basketball

Grade settled bets:

GET /api/v3/fixtures/grade?fixture_id=FIXTURE_ID&sportsbook=kalshi&market=moneyline&selection=candidate_a

Grading outcomes:

GradeMeaning
WonSelection won — full payout
LostSelection lost — no payout
RefundedEvent cancelled or voided — stake returned
Half WonPartial win (Asian handicap/totals)
Half LostPartial loss (Asian handicap/totals)
PendingEvent 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=moneyline

This 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 opportunities

Pattern 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 spread

Pricing 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 = yes or no
  • 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_lay sportsbook 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

EndpointMethodPurpose
/sportsGETList all available sports
/leaguesGETList leagues within a sport
/sportsbooksGETList all supported sportsbooks/exchanges
/marketsGETList available market types for a sport
/fixtures/activeGETGet upcoming and live events
/fixtures/oddsGETCore — Pull odds snapshot with order book and source IDs
/fixtures/odds/historyGETHistorical price movements for backtesting
/fixtures/gradeGETBet settlement / grading
/fixtures/resultsGETEvent results and scores
/stream/oddsGET (SSE)Core — Real-time odds streaming with order book updates
/stream/resultsGET (SSE)Real-time results and score streaming
/parlay/oddsPOSTSGP Pricer — correlation-adjusted combo/parlay pricing
/copilot/parlay/oddsPOSTCopilot Bet Builder — config-driven combo pricing

Best Practices for Prediction Market Makers

Data Architecture

  1. Stream, don't poll — Use /stream/odds as your primary data source. Polling /fixtures/odds introduces latency and wastes rate limit budget.
  2. Snapshot on startup — When your system starts, pull a full snapshot via /fixtures/odds to hydrate your state, then switch to streaming for updates.
  3. Use exclude_fees=true — Always request raw prices and apply your own fee model. Exchange fee tiers vary by volume and may be negotiated.
  4. Request DECIMAL or PROBABILITY format — These are the most useful for quantitative calculations. American odds require conversion before any math.

Execution

  1. Use source_ids for order routing — When you identify an opportunity, use the market_id and selection_id from source_ids to construct your order on the native exchange API.
  2. Respect limits — The max field in limits tells you the most you can trade at the current best price. Size your orders accordingly.
  3. Check order_book depth before trading — Top-of-book size may be small. Check deeper levels to understand the full available liquidity and expected slippage.
  4. Handle Betfair back/lay separately — Query both betfair_exchange and betfair_exchange_lay to reconstruct the full two-sided book.

Risk Management

  1. Monitor results streams — Use /stream/results to track live scores and event status changes for real-time position management.
  2. Use the grading endpoint — Don't rely on your own settlement logic. Use /fixtures/grade to confirm official outcomes.
  3. Track multiple exchanges — Price discrepancies across exchanges can signal information asymmetry. A sudden move on one exchange often predicts moves on others.
  4. 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

  1. Track net exposure per event — Aggregate your positions across exchanges into a single view. Use normalized_selection to match the same outcome across different books.
  2. Use fixture_id as 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.
  3. Automate settlement reconciliation — After an event completes, call /fixtures/grade for each exchange you traded on and reconcile against your expected P&L. Flag any discrepancies immediately.

Latency & Infrastructure

  1. Co-locate your consumer — Place your SSE stream consumer as close to OpticOdds infrastructure as possible to minimize network latency on price updates.
  2. 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.
  3. Maintain a local order book mirror — Rather than parsing the full order_book array on every update, maintain a local copy and apply deltas. This reduces processing time on hot paths.
  4. Timestamp everything — The timestamp field 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

  1. 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.
  2. 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.
  3. Monitor is_live transitions — When a fixture flips from unplayed to live, exchange order books often thin out and spreads widen. This is both a risk and an opportunity for market makers.
  4. Use historical data for regime detection — Pull /fixtures/odds/history to study how order book depth and spreads behave around similar events. Build models that adapt your quoting strategy based on historical patterns.

Rate Limits

  1. 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 /sports call
  • Discover available sports and leagues traded on prediction market exchanges
  • Pull odds for a fixture with exclude_fees=true and inspect the order_book and source_ids fields
  • Set up SSE streaming for real-time order book updates
  • Map source_ids to 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/results and /fixtures/grade

Glossary

TermDefinition
CLOBCentral Limit Order Book — the order matching model used by exchanges like Kalshi and Polymarket
Order BookThe stack of resting orders at various price/size levels on an exchange
Top of BookThe best available price and its size — the first entry in order_book
DepthThe number of price levels in the order book; deeper books mean more liquidity
SpreadThe difference between the best bid and best ask prices
Implied Probability1 / decimal_odds — the market's estimated probability of an outcome
Source IDsNative exchange identifiers (market_id, selection_id) for direct API interaction
SSEServer-Sent Events — the streaming protocol used by OpticOdds for real-time data
LayA bet against an outcome (used on Betfair Exchange) — the other side of a back bet
SettlementThe process of determining the outcome and paying out winning positions
Vig / JuiceThe fee or margin built into odds; exclude_fees=true removes this
TickA single price movement; historical data provides every tick
Binary ContractAn 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.