HomeBlog › Pinnacle Odds API Tutorial: From Zero to Your First Live Board
Guides

Pinnacle Odds API Tutorial: From Zero to Your First Live Board

Pinnacle Odds API Tutorial: From Zero to Your First Live Board

A hands-on walkthrough: get a key, prove the connection, pull a live board, parse it in Python, and handle the errors you'll actually hit.

Pinnacle Odds API Tutorial: From Zero to Your First Live Board

The goal of this post is narrow and practical: get you from nothing to a parsed, live Pinnacle board in about fifteen minutes, and leave you with code that fails gracefully instead of mysteriously. We'll use pinnapi's endpoints, but the structure — authenticate, verify, snapshot, parse, recover — is the same for any odds feed.

What you actually need

That's it. No Pinnacle account; pinnapi is independent.

Step 1 — Grab a trial key

Sign in at the panel and copy your key. Treat it like a password — it's your identity to the API, so keep it out of source control and read it from an environment variable:

export PINNAPI_KEY="your_key_here"

Step 2 — Prove you're connected before you debug a phantom

Half of all "the API is broken" tickets are actually auth or networking issues. Confirm the basics before you write any real logic:

curl -i -H "x-portal-apikey: $PINNAPI_KEY" \
 "https://pinnapi.com/health"

A 200 with a small JSON body means your key works and you can reach the service. A 401 means the key is wrong or a missing/misnamed x-portal-apikey header. A timeout means it's your network or a proxy, not the API. Knowing which of those three you're looking at saves an hour later.

Step 3 — Pull your first live board

curl -H "x-portal-apikey: $PINNAPI_KEY" \
 "https://pinnapi.com/kit/v1/markets?sport_id=1&event_type=live"

You'll get a list of fixtures, each with its markets (moneyline, totals, spreads) and current prices. If the list is empty, there may genuinely be no live soccer right now — switch event_type=live to event_type=prematch to confirm you're getting data at all.

Step 4 — Parse it in Python

import os, requests

KEY = os.environ["PINNAPI_KEY"]
BASE = "https://pinnapi.com/kit/v1"

def get_markets(sport_id=1, event_type="live"):   # 1 = soccer
    r = requests.get(
        f"{BASE}/markets",
        headers={"x-portal-apikey": KEY},
        params={"sport_id": sport_id, "event_type": event_type},
        timeout=5,
    )
    r.raise_for_status()
    return r.json()["events"]

for ev in get_markets():
    ml = ev.get("periods", {}).get("num_0", {}).get("money_line")  # full-match 1X2
    if not ml:
        continue
    print(f'{ev["home"]} vs {ev["away"]}: {ml["home"]} / {ml.get("draw")} / {ml["away"]}')

Note the timeout=5 and raise_for_status(). Those two lines are the difference between a script that hangs forever on a bad night and one that tells you what went wrong.

Step 5 — Drill into one event efficiently

Once you've found a fixture you care about, don't keep pulling the whole board. Request just that fixture:

def get_fixture(fixture_id):
    r = requests.get(
        f"{BASE}/fixtures/{fixture_id}",
        headers={"x-portal-apikey": KEY},
        timeout=5,
    )
    r.raise_for_status()
    return r.json()

On the free tier you have 100 requests/day, so scoping calls to single fixtures is the habit that keeps you under the cap while you're learning.

A word on big tournaments

During something like the 2026 World Cup, live markets update fast and often. REST polling will work for getting started, but you'll feel the staleness: by the time your next poll fires, the price has already moved. That's the moment to graduate from polling to a stream.

The capability worth graduating to: odds drops

The reason people pay for a Pinnacle feed isn't the snapshot — it's catching the move. pinnapi exposes drops over SSE so you're notified the instant a price falls, rather than discovering it on your next poll. In our benchmark that's roughly 15–40 ms end-to-end (measure your own region before relying on it). There's a dedicated SSE walkthrough if you want to go there next.

Troubleshooting, quickly

SymptomMost likely causeFix
401 UnauthorizedMissing or wrong key / wrong header nameRe-check the header exactly
429 Too Many RequestsHit the free-tier capScope calls to single fixtures; upgrade if needed
Empty events listNo events live right nowTry event_type=prematch to confirm data flow
Script hangsNo request timeoutAlways pass timeout=

Takeaway

You now have a working auth flow, a live snapshot, a parser, and the error handling that keeps it alive in production. The natural next step is replacing the poll with a drop stream — that's where the speed actually pays off.

FAQ

Do I need a Pinnacle account? No. pinnapi is independent and not affiliated with Pinnacle.

Is the free tier enough to follow this? Yes — every call here fits inside 100 requests/day.

What languages are supported? Anything that can make an HTTP request; official examples cover curl, Python, Node.js, Go, and PHP.

Get real-time Pinnacle odds in your code

Live & prematch markets with sub-second odds-drop alerts. Free trial key in seconds — no card.

Start free trial