Trading Desk Onboarding: From API Key to Live Pricing in 5 Days

Trading Desk Onboarding - OddsPapi API Blog
How To Guides July 15, 2026

You Just Got B2B Access. Now What?

Your sportsbook signed an OddsPapi enterprise deal on Monday. By Friday, your trading desk is expected to be pricing live markets off the feed. The dashboard hands you a WebSocket endpoint, an API key, and a docs URL, but no runbook for the order you should actually wire things up in.

This is that runbook. Five days, five integration milestones, each one a working Python building block: connect and detect stale books, anchor on the sharp reference price, baseline your closing line value, close the settlement loop, then read exchange depth. Every code sample below was smoke-tested against the live OddsPapi feed on a World Cup 2026 fixture, so the numbers are real, not illustrative.

If you are a bettor and not an operator, you want the Free Odds API guide instead. This post is for the people who run the book, not the people who bet into it.

The Problem: An Odds Feed Is Not a Trading Desk

Buying a data feed is the easy part. The hard part is the integration scaffolding nobody sells you: stale-price detection, a sharp anchor to de-vig against, a CLV audit so you know whether your lines leak value, a settlement loop, and exchange-depth reads for hedging. Most teams discover they need all five only after a sharp customer has been arbing a stale line for three weeks.

The OddsPapi B2B stack ships the raw material for all five. What follows is the assembly order. Across 372 bookmakers including sharps (Pinnacle, SBOBet), exchanges (Betfair, Polymarket, Kalshi), and every major US book, plus free historical price timelines and a WebSocket-first feed, you have everything a trading desk needs without licensing five separate vendors.

The Old Way OddsPapi Onboarding
Scrape 40 books, build your own poller, miss every in-play move One WebSocket: wss://v5.oddspapi.io/ws, 200+ books pushed
Buy a separate “sharp” feed for Pinnacle reference pricing Pinnacle and SBOBet in the same payload as your soft books
Pay per-call for historical lines to compute CLV Free /historical-odds price timelines, opening to close
License a stats vendor just to map fixture IDs for compliance externalProviders block on every fixture (Betradar, Genius, Pinnacle IDs)
Build exchange-depth integration from four different exchange APIs Normalised exchangeMeta back/lay ladders in the odds payload

Day 1: Connect the WebSocket and Detect Stale Books

The feed is WebSocket-first. You connect to the gateway, send a login message with your channel filters, and the server pushes updates as they happen rather than making you poll. The envelope is consistent across every channel:

{
  "channel": "odds",
  "type": "UPDATE",
  "payload": { "...": "..." },
  "ts": 1765497902846,
  "entryId": "1765497902846-3221"
}

Three things to wire up on Day 1, in priority order:

1. Pick your encoding

The receiveType field on the login message controls compression. On a busy in-play Saturday the odds channel is the bandwidth hog, so do not leave this on the default:

receiveType Format Bandwidth
json Plain JSON (default) 1x baseline
binary MessagePack smaller
zstd Compressed JSON ~5-6x smaller
zstd-dict Dictionary-trained zstd ~7-9x on odds

2. Handle resume and replay so a blip is not an outage

The login_ok response carries a resume block: serverEpoch (the gateway session id), serverEntryIds (latest cursor per channel), and resumeWindowMs (typically 60000, sixty seconds of buffered replay). Store the last entryId you processed per channel. If you reconnect inside the window, you replay missed updates with no gaps. If your cursor is older than the buffer, the server sends a snapshot_required control message (reasons: server_restarted, resume_window_exceeded, client_backpressure) and you fall back to a fresh REST snapshot for those channels only. Two-tier recovery: fast replay or one REST call, never an open-ended hole.

3. Watch the staleOdds flag

This is the single most important field for a trading desk. Bookmaker connectivity is surfaced via staleOdds: when a book’s upstream goes quiet, its prices are flagged rather than silently frozen. A frozen price you treat as live is exactly how a sharp customer arbs you. The full channel architecture, the staleOdds deep-dive, and the participantsRotated flag are covered in the Sportsbook Odds Feed pipeline post. Read that before you go live.

Day 2: Anchor on the Sharp Reference Price

You cannot price a market without a fair-probability anchor, and the cleanest one in the payload is Pinnacle. Day 2 is wiring up a de-vig function and pointing it at the sharp line. Here is the live pull on a real World Cup 2026 group fixture, Norway vs Senegal, captured June 21 with 18 books on the 1X2 market (market id 101):

import requests

API_KEY = "YOUR_API_KEY"
BASE = "https://api.oddspapi.io/v4"

def get_odds(fixture_id, books=None):
    params = {"apiKey": API_KEY, "fixtureId": fixture_id}
    if books:
        params["bookmakers"] = ",".join(books)
    return requests.get(f"{BASE}/odds", params=params).json()

def price(book_odds, slug, market="101", outcome="101"):
    """Current decimal price for one book/market/outcome, or None."""
    try:
        node = book_odds[slug]["markets"][market]["outcomes"][outcome]["players"]["0"]
        return node["price"] if node.get("active") and node["price"] > 0 else None
    except (KeyError, TypeError):
        return None

def devig_proportional(prices):
    """prices: {outcome_id: decimal}. Returns {outcome_id: (fair_prob, fair_decimal)}."""
    inv = {k: 1 / v for k, v in prices.items()}
    overround = sum(inv.values())
    return {k: (inv[k] / overround, overround / inv[k]) for k in inv}, overround

fixture = "id1000001666457012"  # Norway v Senegal, World Cup 2026
bo = get_odds(fixture)["bookmakerOdds"]

pin = {o: price(bo, "pinnacle", "101", o) for o in ["101", "102", "103"]}
fair, overround = devig_proportional(pin)
print(f"Pinnacle overround: {(overround - 1) * 100:.2f}%")
for o, label in [("101", "Norway"), ("102", "Draw"), ("103", "Senegal")]:
    p, d = fair[o]
    print(f"  {label:8} raw {pin[o]:.3f}  ->  fair {p*100:.1f}%  ({d:.3f})")

Real output from that fixture:

Outcome Pinnacle raw No-vig fair % Fair decimal
Norway 2.28 42.4% 2.356
Draw 3.62 26.7% 3.741
Senegal 3.14 30.8% 3.245

Pinnacle’s overround here was 3.33%, against soft books quoting wider. Once you have the fair line, your own price is just fair plus your target margin. The proportional method shown here is the fast one; for the power and Shin alternatives (they disagree by up to 9% on longshots) see the no-vig methods comparison, and for blending multiple sharps into a consensus anchor see consensus odds. The full auto-pricing engine that turns this into quoted lines is the white-label price feed post.

Day 3: Baseline Your Closing Line Value

Before you trust your own prices, you need a yardstick: how did the sharp line move from open to close, and are your lines tracking it? That is CLV, and the free /historical-odds endpoint gives you the full price timeline at no extra cost. Same fixture, Pinnacle’s complete history on the Norway moneyline:

def historical(fixture_id, book):
    params = {"apiKey": API_KEY, "fixtureId": fixture_id, "bookmakers": book}
    return requests.get(f"{BASE}/historical-odds", params=params).json()

h = historical("id1000001666457012", "pinnacle")
# NOTE: historical top key is "bookmakers" (not "bookmakerOdds"),
# and players["0"] is a LIST of snapshots, not a single dict.
snaps = h["bookmakers"]["pinnacle"]["markets"]["101"]["outcomes"]["101"]["players"]["0"]

opening, closing = snaps[0], snaps[-1]
prices = [s["price"] for s in snaps]
print(f"{len(snaps)} snapshots")
print(f"open  {opening['price']}  @ limit {opening['limit']}  ({opening['createdAt'][:10]})")
print(f"latest {closing['price']}  @ limit {closing['limit']}  ({closing['createdAt'][:10]})")
print(f"range {min(prices)} -> {max(prices)}")

Output: 96 snapshots between May 11 and June 21. The line opened at 2.17 with a limit of just 500, and by the latest pull sat at 2.28 with the limit raised to 15,000. That limit growth is the tell: Pinnacle posts early with small limits and scales them up as the market matures and its confidence rises. The price itself drifted across a 1.98 to 2.41 band over six weeks. Your CLV audit is just this timeline diffed against where your own book closed each market. The operator-grade audit pipeline (per-fixture and batched across a slate) is in the CLV line-audit post, and if you want these timelines exported to CSV or Excel for your analysts, see historical odds to CSV.

Day 4: Close the Settlement Loop

A market closes, you need bets graded, the ledger updated. On the enterprise WebSocket tier the settlement endpoints return per-outcome results (won/lost/void), final scores, and margins. Worth being precise here so you do not wire up against the wrong layer: the public v4 REST API does not expose a settlement or results endpoint, and the /fixtures feed carries status (statusName: Pre-Game / Live / Finished) but no scores or stats. If you are on v4 rather than the B2B WebSocket, you grade off the price collapse instead: when an outcome’s price drops to ~1.0 and the fixture flips to Finished, that outcome resolved. The honest mechanics of what the schedule feed does and does not carry are in the Free Sports Data API post.

def is_settled(fixture_status, outcome_price):
    """v4 proxy for settlement: Finished status + collapsed price."""
    return fixture_status == "Finished" and outcome_price is not None and outcome_price <= 1.05

The flip side of settlement is risk: knowing when your own book is the soft leg that sharp money is hitting before the market closes. That detection scanner, comparing your quoted price against the market-wide consensus and flagging exploitable gaps, is the arb-detection risk-signal post. And for the compliance side, joining your OddsPapi fixtureId to a licensed stats feed for regulatory reporting, every fixture ships an externalProviders block (Betradar fills 100%, Pinnacle 57%) covered in fixture mapping for compliance.

Day 5: Read Exchange Depth for Hedging

By Day 5 you are quoting lines and want to hedge directional exposure. For that you need real liquidity, not just a top-of-book price, and the exchanges in the feed ship a full depth-of-book ladder under exchangeMeta. Same Norway vs Senegal fixture, the Norway back ladder on the two prediction-market exchanges:

def ladder(book_odds, slug, market="101", outcome="101"):
    node = book_odds[slug]["markets"][market]["outcomes"][outcome]["players"]["0"]
    em = node.get("exchangeMeta")
    if not em:  # sportsbooks ship null or {} here
        return None
    return em.get("back", []), em.get("lay", [])

for slug in ["kalshi", "polymarket"]:
    back, lay = ladder(bo, slug)
    print(slug, "best back:", back[0], "| best lay:", lay[0])
Exchange Best back (Norway) Depth at level Best lay
Kalshi 2.273 $1.39M (limit $612K) 1.754
Polymarket 2.326 $327K (limit $141K) 1.724

Each ladder level is a dict of {cents, price, size, limit}: price is decimal odds, size is total liquidity, limit is stake depth available to match, and cents is the native 0-1 share price on prediction markets. Note the shape varies across exchanges (Betfair uses availableToBack/availableToLay, some legacy books ship a flat scalar) so parse defensively. The continuous spread-quoting and directional-hedge signal loop built on these ladders is the market-maker real-time feed post.

The Five-Day Stack, Assembled

Day Milestone OddsPapi primitive Deep-dive
1 Connect + stale detection WebSocket, staleOdds, resume/replay Sportsbook Odds Feed (3053)
2 Sharp reference + de-vig Pinnacle/SBOBet in payload White-Label Feed (3068)
3 CLV baseline Free /historical-odds timelines CLV Line Audit (3056)
4 Settlement + risk loop Settlement (B2B), externalProviders Arb Detection (3077)
5 Exchange depth + hedging exchangeMeta ladders Market Maker Feed (3059)

That is a trading desk wired from a cold API key to live pricing in a working week, on one vendor, with sharps, exchanges, and free historical data in the same feed. The margin-trend monitoring you layer on top of all of this (benchmarking your vig against the sharp market over time) is the bookmaker margin analytics post.

Stop Licensing Five Vendors. Get Your Key.

Sharp reference pricing, exchange depth, free historical timelines, and a WebSocket-first feed across 372 bookmakers, all from one integration. Get your free OddsPapi API key and start on Day 1 today.

Frequently Asked Questions

What is a sports trading API for sportsbook operators?

It is a feed built for the people setting and managing lines rather than betting into them. Beyond raw odds it surfaces a sharp reference price to de-vig against, stale-book flags, historical timelines for CLV, exchange depth for hedging, and settlement data. OddsPapi delivers all of these across 372 bookmakers on a WebSocket-first feed.

How long does it take to integrate an odds feed into a trading desk?

With the primitives in place, a working week. This playbook sequences it: Day 1 WebSocket and stale detection, Day 2 sharp de-vig anchor, Day 3 CLV baseline, Day 4 settlement and risk loop, Day 5 exchange depth. Each milestone is a tested Python building block.

Does OddsPapi provide settlement and results data?

The enterprise WebSocket tier exposes settlement endpoints returning per-outcome results, final scores, and margins. The public v4 REST API does not carry scores or results; v4 users grade off the price collapse to ~1.0 plus the Finished fixture status. Map to a licensed stats feed via the externalProviders IDs for regulatory settlement.

How do I get the sharp (Pinnacle) reference price?

Pinnacle and SBOBet arrive in the same /odds payload as your soft books, keyed by slug. De-vig the sharp line with the proportional, power, or Shin method to get a fair-probability anchor, then add your target margin to set your own price.

Can I read exchange order-book depth for hedging?

Yes. Exchanges (Kalshi, Polymarket, Betfair) ship a full back/lay ladder under exchangeMeta, with price, size, and limit at each level. On the Norway vs Senegal example, Kalshi showed $1.39M of liquidity at the top back level. Parse defensively since the ladder shape varies by exchange.