Free trial · 7 days

Get a free key

We email you a link; opening it shows your API key. Then message @oddspush on Telegram and we activate your 7-day free trial. No card.

By continuing you agree to the Terms and the Privacy Policy.

Already have a key?

Lost your key?

Real-time odds feed for trading bots

Pinnacle odds,
ahead of the market.

Every Pinnacle price change, streamed over WebSocket the moment it happens. Sub-second delivery, every market, 13 sports.

wss://feed.oddspush.com/v1/stream
Demo data
Live events with decimal odds. Demo data. Choose an event to inspect it.
Event Home Draw Away Handicap Total
1.46 4.45 8.10 −1 2.06 +1 1.80 o1.5 1.94 u1.5 1.91
2.31 3.08 3.62 −0.25 2.02 +0.25 1.84 o2.25 1.96 u2.25 1.89
1.19 No draw 5.15 −4.5 1.92 +4.5 1.93 o21.5 1.88 u21.5 1.97
1.42 No draw 2.98 −6.5 1.93 +6.5 1.92 o224.5 1.91 u224.5 1.94
1.61 No draw 2.42 −1.5 2.64 +1.5 1.51 o5.5 1.95 u5.5 1.90
1.86 No draw 1.98 −1.5 1.92 +1.5 1.93 o161.5 1.90 u161.5 1.95

Pinnacle → your bot · Europe

32ms

median 29 ms · 1.4 msg/s

SOC Premier League 67′

Arsenal 1 to 0 Chelsea

Home 1.46
Draw 4.45
Away 8.10

Arsenal to win · last 60 s

2,418 messages 6 events

The latest odds message for a moneyline, as the feed sends it. Prices are American odds. Demo data.

{"type": "odds",  "data": {"type": "live", "sport_id": 1,    "topic": "matchups/reg/sp/29/live/ld", "op": "upd",    "rec": {"id": 1638780974,      "markets": [{"key": "s;0;m",        "version": 3724875515, "status": "open",        "prices": [          { "designation": "home", "price": -217 },          { "designation": "draw", "price": 345 },          { "designation": "away", "price": 710 }        ]}]},    "ts": 1792512311307}}
{"type": "odds",  "data": {"type": "live", "sport_id": 1,    "topic": "matchups/reg/sp/29/live/ld",    "op": "upd",    "rec": {"id": 1638780974,      "markets": [{"key": "s;0;m",        "version": 3724875515,        "status": "open",        "prices": [          { "designation": "home",            "price": -217 },          { "designation": "draw",            "price": 345 },          { "designation": "away",            "price": 710 }        ]}]},    "ts": 1792512311307}}

OddsPush in numbers

Pinnacle → your bot · Europe
about 30 ms
From the moment Pinnacle publishes a price to the moment it reaches a bot hosted in Europe.
Events with open markets
3,000+
Live and upcoming fixtures, main events and their child records.
WebSocket you need
1
Filter by sport and event on the same connection.

Live13 sports

  • 01 Soccer
  • 02 Tennis
  • 03 Basketball
  • 04 Hockey
  • 05 American Football
  • 06 Baseball
  • 07 Rugby
  • 08 MMA
  • 09 Boxing
  • 10 Volleyball & handball
  • 11 Esports
  • 12 Golf
  • 13 Cricket
Markets

How it works

Pinnacle updates.
You get it in milliseconds.

The moment Pinnacle changes a price, our relay picks it up and pushes it to you over WebSocket, exactly as published. No polling, no waiting for the next refresh.

Source

Pinnacle

Arsenal v Chelsea

Moneyline · Arsenal 2.10

Relay

OddsPush relay

1 in to 3 out, same bytes

  • Dublin +27 ms

    Arsenal ML 2.10

  • London +30 ms

    Arsenal ML 2.10

  • Frankfurt +33 ms

    Arsenal ML 2.10

0ms Pinnacle publishes the price Every change, from a connection we keep alive for you.

Fan-out Received once, sent to every client Byte for byte as published: nothing rounded, nothing reordered.

~30 ms On your servers, in Europe Your own queue: a slow client never holds back the others.

Integration

Connected in 5 minutes.

Our docs come in a version written for AI coding agents. Download one file, hand it to Claude Code, Codex or Cursor, and it builds the client for you: login, subscription, a live price book and reconnects.

  1. Paste this into your agent 1 min

    The link is our guide for coding agents: the whole protocol, the rules for a correct price book and two reference clients, in one Markdown file.

    Read https://oddspush.com/oddspush-agent-guide.md and build a Python service that streams soccer and basketball odds into a live price book. My key is in ODDSPUSH_KEY.

    Claude Code, Codex, Cursor or any agent that can open a link. Without web access, download the guide (76 KB) into your project. Also at /llms.txt.

  2. Review it and run it 4 min

    The agent asks what the guide leaves to you, writes the client and runs the guide's checklist. Set ODDSPUSH_KEY on the one server that runs your bot, and start it.

Your coding agent · ~/my-bot 3:28 to first odds
You

Read https://oddspush.com/oddspush-agent-guide.md and build a Python service that streams soccer and basketball odds into a live price book. My key is in ODDSPUSH_KEY.

Agent

Read the guide

What should happen with each price?

You

Print the moneyline when it moves.

Agent

Wrote client.py and its tests

Logged in, snapshot of 1,255 events

Checklist passed, reconnect in 0.6 s

Running, about 11 updates a second:

The code it writes, in short

Minimal client

# pip install websocketsimport asyncio, json, osimport websocketsURL = "wss://feed.oddspush.com/v1/stream"KEY = os.environ["ODDSPUSH_KEY"]  # opk_…book = {}  # event id -> {market key: market}def merge(rec):    markets = book.setdefault(rec["id"], {})    for m in rec.get("markets") or []:        k = m.get("key")        old = markets.get(k, {}).get("version", -1)        if k is None or m.get("version", 0) <= old:            continue  # stale or reordered: skip        if m.get("status") == "open":            markets[k] = m        else:            markets.pop(k, None)  # closed: remove itasync def main():    # compression=None: deflate only adds latency    async with websockets.connect(URL, compression=None,                                  max_size=2**24) as ws:        send = lambda msg: ws.send(json.dumps(msg))        await send({"type": "login", "token": KEY})        welcome = json.loads(await ws.recv())        if welcome["type"] == "error":            raise SystemExit(welcome["code"])        await send({"type": "subscribe", "sports": [1],                    "channels": ["ld", "both"]})        async for raw in ws:            msg = json.loads(raw)            kind = msg["type"]            if kind == "odds":                data = msg["data"]                rec = data.get("rec") or {}                channel = str(data.get("topic")).rsplit("/")[-1]                if data.get("op") == "del":                    book.pop(rec.get("id"), None)                elif "id" in rec and channel in ("ld", "both"):                    merge(rec)  # other channels are not prices            elif kind == "snapshot":                for rec in msg["events"]:                    merge(rec)            elif kind == "missed" or msg.get("status") == "up":                await send({"type": "snapshot"})asyncio.run(main())
// npm install wsimport WebSocket from "ws";const KEY = process.env.ODDSPUSH_KEY; // opk_…const book = new Map(); // event id -> Map(market key -> market)const ws = new WebSocket("wss://feed.oddspush.com/v1/stream", {  perMessageDeflate: false, // deflate only adds latency});const send = (msg) => ws.send(JSON.stringify(msg));function merge(rec) {  if (!book.has(rec.id)) book.set(rec.id, new Map());  const markets = book.get(rec.id);  for (const m of rec.markets ?? []) {    const old = markets.get(m.key)?.version ?? -1;    // stale or reordered: skip    if (m.key === undefined || (m.version ?? 0) <= old) continue;    if (m.status === "open") markets.set(m.key, m);    else markets.delete(m.key); // closed: remove it  }}ws.on("open", () => send({ type: "login", token: KEY }));ws.on("message", (raw) => {  const msg = JSON.parse(raw);  if (msg.type === "welcome") {    send({      type: "subscribe",      sports: [1],      channels: ["ld", "both"],    });  } else if (msg.type === "error") {    console.error(msg.code);  } else if (msg.type === "odds") {    const { op, rec = {}, topic = "" } = msg.data;    if (op === "del") book.delete(rec.id);    // other channels are not prices    else if (rec.id && /\/(ld|both)$/.test(topic)) merge(rec);  } else if (msg.type === "snapshot") {    msg.events.forEach(merge);  } else if (msg.type === "missed" || msg.status === "up") {    send({ type: "snapshot" });  }});
# Log in within 15 s, from the machine that runs your bot. npm install -g wscat wscat -c wss://feed.oddspush.com/v1/streamConnected (press CTRL+C to quit) {"type":"login","token":"opk_…"} {"type":"welcome","feed":{"alive":true,"ready":true}, …} {"type":"subscribe","sports":[1],"channels":["ld","both"]} {"type":"subscription","filter":{"sports":[1], …}} {"type":"odds","data":{"type":"live","sport_id":1, …}} {"type":"snapshot","part":0,"final":false,"events":[ …

An odds message

message.json Trimmed for display
{"type": "odds", "data": {   "type": "live",  "sport_id": 1,  "topic": "matchups/reg/sp/29/live/ld",   "op": "upd",  "rec": {    "id": 1638780974,     "type": "matchup",    "startTime": "2026-10-20T16:00:00Z",    "participants": [      {"name": "Lazio", "alignment": "home"},      {"name": "Roma", "alignment": "away"}    ],    "state": {"state": 1, "minutes": 31},    "markets": [      {        "key": "s;0;m",         "type": "moneyline",        "period": 0,        "version": 3772905262,         "prices": [          {"designation": "home", "price": 145},          {"designation": "away", "price": 215},          {"designation": "draw", "price": 230}         ],        "isAlternate": false,        "status": "open"       },      … 41 more markets    ]  },  "ts": 1792512282138 }}
  1. Line 1: type names the message. data is the frame as published.
  2. Line 4: The last segment is the channel. Any we do not list is other.
  3. Line 7: Event id. Key your book on it, or follow it alone with events. On "op": "del" the event is gone: drop it.
  4. Line 17: Unique per event. Period 0 is the full match, m the moneyline.
  5. Line 20: Keep the highest per key. Equal to or lower than yours: stale, skip it.
  6. Line 24: American odds, as quoted: 230 is +230, or 3.30 decimal.
  7. Line 27: Anything but open means the market is closed: remove it.
  8. Line 32: When it was published, in Unix ms.

Pricing

Two ways in.
The same feed.

Pull the book over REST, or have every change pushed to you. All 13 sports on both. Run it on your own bot free for 7 days before you pay anything.

7 days Free

Free trial

Start here

The real feed, REST and WebSocket, on your own bot. Pay only if it earns its place.

Try it free for 7 days

No card. Sign up, then we switch it on.

REST

Pull the whole book whenever your model asks for it.

On request as fresh as you poll

$69 / month

$62 / month instead of $69

$186 for 3 months, paid once Save 10%

$55.17 / month instead of $69

$331 for 6 months, paid once Save 20%

  • REST API: events, markets, odds
  • All 13 sports
  • One key, one IP at a time
  • Not included: Live WebSocket stream
  • The same feed

    Every plan and the trial get the same prices at the same moment. Nothing sampled.

  • Crypto, prepaid

    USDC, USDT, BTC and more. No card, no auto-renewal: you renew when you choose.

  • Switch any time

    Move between paid plans whenever you like: the days you have left carry over, pro rata.

One key per account, used from one IP at a time. Every limit, plan by plan

FAQ

Questions, answered.

Still deciding?

Run it on your own bot for 7 days. If it does not earn its place, let it end.

Try it free for 7 days

Or ask us: support@oddspush.com

How does the free trial work?

Sign up with your email and open the link we send: your key is on that page. Then message @oddspush on Telegram with the email of your account and we switch the trial on. The 7 days count from that moment.

You get REST + WebSocket, all 13 sports and real prices, on your own bot. No card, nothing to cancel: when it ends, pick a plan or let it go. One trial per person.

REST or REST + WebSocket: which one do I need?

REST ($69 a month): your bot asks for the book when it needs it. Enough for models that price before kick-off or check every few seconds.

REST + WebSocket ($138 a month): every change is pushed to you about ~30 ms after Pinnacle publishes it, and REST comes with it. If you trade on price moves, this is the one.

How fast is it, really?

~30 ms from Pinnacle publishing a price to a bot in Europe. Our relay runs next to the source; from further away, add the network time between your server and Western Europe.

If every millisecond matters, host your bot in Europe.

What exactly do I get?

Pinnacle's odds for 13 sports: moneyline, spreads, totals and the rest of each live market, plus the pre-match prices that travel on the live feed. Each message carries the original record byte for byte: nothing rounded, renamed or held back.

On connect you get a snapshot of the current book, then every change. Formats and examples are in the docs.

Can I run my bot from more than one machine?

Your key works from one IP at a time, and there is nothing to set. When it connects from a new IP, that IP takes over and the connection from the old one closes. So run your bot from one server: a second machine with the same key kicks the first.

One key per account, and only we change it. If you lose it or think it has leaked, message @oddspush on Telegram and we issue a new one; the old one stops working.

How do I pay, and can I cancel?

In crypto, from your account: USDC, USDT, BTC or another coin, no card. You prepay 1, 3 or 6 months, and the longer terms cost less.

Nothing renews on its own, so there is nothing to cancel: you renew when you choose. Move between paid plans and the days you have left carry over.