Player Props Analyzer Example - Python

Analyze player prop bets by fetching props for an event and comparing lines across every Australian bookmaker.

What you'll build

A script that:

  • Fetches all player props for an AFL event
  • Compares lines across bookmakers
  • Shows consensus lines
  • Identifies outlier books
  • Highlights potential value bets

Perfect for: Finding value in player props, comparing bookmaker offerings, prop research

Prerequisites

  • Python 3.8+
  • SockOdds API key (free)
  • Basic Python

Complete code

Step 1: Setup project

mkdir props-analyzer && cd props-analyzer
python -m venv venv && source venv/bin/activate
pip install requests

Step 2: Create analyzer.py

# analyzer.py
import os, sys, statistics, requests

API_KEY = os.environ["SOCKODDS_KEY"]
BASE = "https://api.sockodds.com/v2"
LEAGUE = sys.argv[1] if len(sys.argv) > 1 else "AFL"
TEAM_SIDES = {"home", "away", "all"}

def fetch_event(league):
    r = requests.get(f"{BASE}/events", params={"leagueID": league, "oddsAvailable": "true", "limit": 1}, headers={"x-api-key": API_KEY}); r.raise_for_status()
    data = r.json()["data"]
    return data[0] if data else None

def is_player_prop(odd):
    return odd["statEntityID"] not in TEAM_SIDES          # the one check that identifies a prop

def analyze(event):
    players = event.get("players", {}); rows = []
    for odd in event["odds"].values():
        if not is_player_prop(odd) or odd["betTypeID"] != "ou" or odd["sideID"] != "over": continue
        lines = {b: float(q["overUnder"]) for b, q in odd["byBookmaker"].items() if q.get("available") and q.get("overUnder")}
        prices = {b: q["decimal"] for b, q in odd["byBookmaker"].items() if q.get("available") and q.get("decimal")}
        if len(lines) < 2: continue
        consensus = statistics.median(lines.values())
        name = players.get(odd["statEntityID"], {}).get("name", odd["statEntityID"])
        for book, line in lines.items():
            if line != consensus:
                rows.append((abs(line - consensus), name, odd["statID"], book, line, consensus, prices.get(book)))
    return sorted(rows, reverse=True)

if __name__ == "__main__":
    e = fetch_event(LEAGUE)
    if not e: sys.exit(f"no {LEAGUE} events with odds")
    print(f"{e['teams']['away']['names']['medium']} @ {e['teams']['home']['names']['medium']}  ({e['eventID']})")
    props = [o for o in e["odds"].values() if is_player_prop(o)]
    print(f"{len(props)} player-prop odds across {len({o['statID'] for o in props})} stats\n")
    print(f"{'player':22} {'stat':14} {'book':14} {'line':>6} {'consensus':>10} {'price':>6}")
    for diff, name, stat, book, line, cons, price in analyze(e)[:25]:
        flag = "  <- value?" if diff >= 1 else ""
        print(f"{name:22} {stat:14} {book:14} {line:6.1f} {cons:10.1f} {price or 0:6.2f}{flag}")

Step 3: Run it

SOCKODDS_KEY=so_live_… python analyzer.py AFL

Expected output

Hawthorn @ Fremantle  (afl_2026-09-03_fremantle_vs_hawthorn)
236 player-prop odds across 4 stats

player                 stat           book             line  consensus  price
Caleb Serong           disposals      pointsbet        28.5       29.5   1.95  <- value?
Jai Newcombe           disposals      tab              26.5       27.5   1.91  <- value?

How it works

1. Identify player props

Any market whose statEntityID is not home/away/all.

2. Process bookmaker odds

Each book has its own overUnder and decimal; skip unavailable ones.

3. Calculate consensus

The median line across books.

4. Find outliers

Books whose line differs from the consensus, sorted by the size of the gap.

5. Identify value

A lower over line at the same price is worth a look — then check fairOdds on Base.

Enhancements

Track props over time

# store (eventID, oddID, book, line, price, lastUpdatedAt) each run and diff

Filter by prop type

props = [o for o in props if o["statID"] == "disposals"]

Export to spreadsheet

import csv  # write rows with csv.writer

Add EV calculations

# on Base: edge = 1/decimal(fairOdds) - 1/price  (see the +EV use case)

Troubleshooting

"No player props found"

Props are posted a day or three out; try a league in season or includeFinished for a recent event.

Props missing for some players

Not every book prices every player.

Player names showing as IDs

The players map only carries players the source resolved; fall back to the id.

Next steps

Combine with other examples

Learn more

Need help?FAQ · Email · Contact