UK Player Props API: Compare Goalscorer Odds Across All Bookmakers (Python)
Player prop markets — anytime goalscorer, player shots, player cards, player assists — are the fastest-growing segment of UK football betting. Every major bookmaker now offers them, and the pricing varies wildly between books. The problem? Getting player prop odds programmatically from UK bookmakers has been nearly impossible.
Most odds APIs either don't carry player props at all (Sportmonks — 42 markets, no props), limit them to US sports (The Odds API), or only have them for a single bookmaker (Odds-API.io confirmed they only have player props for Bet365 from UK books). Nobody covers player props across Bet365, Sky Bet, Paddy Power, William Hill, Ladbrokes, Coral, Betfred, and the rest of the UK high street.
UKOddsApi does. This tutorial shows you how to pull player prop odds from all 25+ UK bookmakers and build tools that compare goalscorer, shots, cards, and assists prices across every bookmaker simultaneously.
What player prop markets are available
UKOddsApi provides player-level and team-level prop markets through the package=full parameter. Use the markets endpoint to discover what's available:
import requests
API_KEY = "your_api_key_here"
BASE_URL = "https://api.ukoddsapi.com"
headers = {"X-Api-Key": API_KEY}
# Discover all available market types
response = requests.get(
f"{BASE_URL}/v1/football/markets",
headers=headers,
params={"package": "full"}
)
markets = response.json()["markets"]
# Group by category
groups = {}
for m in markets:
g = m.get("market_group", "other")
if g not in groups:
groups[g] = []
groups[g].append(m["market_name"])
for group, market_list in sorted(groups.items()):
print(f"\n📂 {group.upper()}")
for name in market_list:
print(f" • {name}")
Example output:
📂 MAIN
• Win Market
• Both Teams to Score
• Over/Under 2.5 Goals
• Double Chance
• Draw No Bet
📂 GOALS
• Anytime Goalscorer
• First Goalscorer
• Last Goalscorer
• Player to Score 2+
📂 SHOTS
• Player Shots Over/Under
• Player Shots on Target Over/Under
📂 CARDS
• Player to be Carded
• Player to be Shown Red Card
📂 ASSISTS
• Player Assists Over/Under
📂 CORNERS
• Total Corners Over/Under
• Total Home Corners
• Total Away Corners
📂 BOOKINGS
• Total Match Booking Points
• Team Booking Points
Step 1: Pull goalscorer odds from all UK bookmakers
from datetime import date
# Get today's fixtures
events = requests.get(
f"{BASE_URL}/v1/football/events",
headers=headers,
params={
"schedule_date": date.today().isoformat(),
"league": "premier-league",
}
).json()["events"]
event_id = events[0]["event_id"]
# Get player props with package=full
response = requests.get(
f"{BASE_URL}/v1/football/events/{event_id}/odds",
headers=headers,
params={
"package": "full",
"market": "goalscorer", # Filter to goalscorer markets
"odds_format": "decimal",
}
)
odds = response.json()
print(f"⚽ {odds['event_title']}")
print(f" {odds['summary']['bookmakers_count']} bookmakers providing player props\n")
for market in odds["markets"]:
print(f"📈 {market['market_name']}\n")
# Group by player
players = {}
for sel in market["selections"]:
pname = sel["selection_name"]
if pname not in players:
players[pname] = []
players[pname].append({
"bookmaker": sel["bookmaker_name"],
"code": sel["bookmaker_code"],
"odds": sel["odds"],
})
# Sort players by shortest price (most likely scorers first)
sorted_players = sorted(
players.items(),
key=lambda x: min(o["odds"] for o in x[1])
)
for pname, bookies in sorted_players[:8]:
bookies.sort(key=lambda x: -x["odds"]) # Best value first
best = bookies[0]
worst = bookies[-1]
spread = round(((best["odds"] / worst["odds"]) - 1) * 100, 1)
print(f" 🎯 {pname}")
print(f" Best: {best['odds']} @ {best['bookmaker']}")
print(f" Worst: {worst['odds']} @ {worst['bookmaker']}")
print(f" Spread: {spread}% ({len(bookies)} bookmakers)")
print()
Output:
⚽ Arsenal vs Chelsea
22 bookmakers providing player props
📈 Anytime Goalscorer
🎯 Kai Havertz
Best: 2.80 @ BoyleSports
Worst: 2.50 @ Bet365
Spread: 12.0% (18 bookmakers)
🎯 Bukayo Saka
Best: 3.40 @ Coral
Worst: 3.00 @ Bet365
Spread: 13.3% (20 bookmakers)
🎯 Nicolas Jackson
Best: 3.60 @ William Hill
Worst: 3.10 @ Sky Bet
Spread: 16.1% (17 bookmakers)
🎯 Cole Palmer
Best: 4.20 @ BetVictor
Worst: 3.60 @ Paddy Power
Spread: 16.7% (19 bookmakers)
Notice those spreads: 12-17% price differences between UK bookmakers on the same player. That's significant value being left on the table by anyone who only checks one bookmaker.
Step 2: Build a player props value finder
This tool scans all fixtures and finds the biggest pricing discrepancies — where one bookmaker is offering substantially higher odds than the rest:
def find_prop_value(fixtures, market_filter="goalscorer", min_spread_pct=10):
"""
Find player prop markets with the biggest bookmaker disagreements.
Higher spread = bigger value opportunity.
"""
opportunities = []
for fixture in fixtures:
response = requests.get(
f"{BASE_URL}/v1/football/events/{fixture['event_id']}/odds",
headers=headers,
params={
"package": "full",
"market": market_filter,
"odds_format": "decimal",
}
)
if response.status_code != 200:
continue
odds = response.json()
for market in odds["markets"]:
players = {}
for sel in market["selections"]:
pname = sel["selection_name"]
if pname not in players:
players[pname] = []
players[pname].append({
"bookmaker": sel["bookmaker_name"],
"odds": sel["odds"],
})
for pname, bookies in players.items():
if len(bookies) < 3:
continue
bookies.sort(key=lambda x: -x["odds"])
best = bookies[0]
worst = bookies[-1]
# Calculate average excluding best and worst
mid_odds = [b["odds"] for b in bookies[1:-1]]
avg_odds = sum(mid_odds) / len(mid_odds)
spread_pct = ((best["odds"] / worst["odds"]) - 1) * 100
vs_avg_pct = ((best["odds"] / avg_odds) - 1) * 100
if spread_pct >= min_spread_pct:
opportunities.append({
"match": fixture["event_title"],
"market": market["market_name"],
"player": pname,
"best_odds": best["odds"],
"best_bookie": best["bookmaker"],
"worst_odds": worst["odds"],
"worst_bookie": worst["bookmaker"],
"avg_odds": round(avg_odds, 2),
"spread_pct": round(spread_pct, 1),
"vs_avg_pct": round(vs_avg_pct, 1),
"bookmaker_count": len(bookies),
})
return sorted(opportunities, key=lambda x: -x["spread_pct"])
# Scan Premier League fixtures
fixtures = requests.get(
f"{BASE_URL}/v1/football/events",
headers=headers,
params={"league": "premier-league"}
).json()["events"]
print("🔍 Scanning player props for value...\n")
opps = find_prop_value(fixtures, market_filter="goalscorer", min_spread_pct=15)
print(f"🎯 Found {len(opps)} opportunities with 15%+ spread\n")
print(f"{'Player':<22}{'Match':<28}{'Best':>7}{'Bookie':<16}{'Avg':>7}{'Spread':>8}")
print(f"{'-'*88}")
for opp in opps[:20]:
print(
f"{opp['player'][:21]:<22}"
f"{opp['match'][:27]:<28}"
f"{opp['best_odds']:>7.2f}"
f"{opp['best_bookie'][:15]:<16}"
f"{opp['avg_odds']:>7.2f}"
f"{opp['spread_pct']:>+7.1f}%"
)
Step 3: Compare shots and cards props
Player props go far beyond goalscorer markets. Here's how to pull shots on target and cards data:
# Player shots
response = requests.get(
f"{BASE_URL}/v1/football/events/{event_id}/odds",
headers=headers,
params={
"package": "full",
"market": "shots",
"odds_format": "decimal",
}
)
shots = response.json()
print(f"🎯 Player Shots — {shots['event_title']}\n")
for market in shots["markets"]:
print(f" 📈 {market['market_name']}")
# Group by player
players = {}
for sel in market["selections"]:
pname = sel["selection_name"]
if pname not in players:
players[pname] = []
players[pname].append({
"bookmaker": sel["bookmaker_name"],
"odds": sel["odds"],
})
for pname, bookies in list(players.items())[:5]:
best = max(bookies, key=lambda x: x["odds"])
print(f" {pname}: {best['odds']} @ {best['bookmaker']} (best of {len(bookies)} bookmakers)")
print()
# Player cards
response = requests.get(
f"{BASE_URL}/v1/football/events/{event_id}/odds",
headers=headers,
params={
"package": "full",
"market": "card",
"odds_format": "decimal",
}
)
cards = response.json()
print(f"🟨 Player Cards — {cards['event_title']}\n")
for market in cards["markets"]:
print(f" 📈 {market['market_name']}")
players = {}
for sel in market["selections"]:
pname = sel["selection_name"]
if pname not in players:
players[pname] = []
players[pname].append({
"bookmaker": sel["bookmaker_name"],
"odds": sel["odds"],
})
for pname, bookies in sorted(players.items(), key=lambda x: min(b["odds"] for b in x[1]))[:5]:
best = max(bookies, key=lambda x: x["odds"])
shortest = min(bookies, key=lambda x: x["odds"])
print(f" {pname}: {best['odds']} (best) — {shortest['odds']} (shortest) across {len(bookies)} bookmakers")
print()
Step 4: Full player props comparison tool
"""
player_props_scanner.py
Compare player prop odds across all UK bookmakers.
Find value on goalscorer, shots, cards, and assists markets.
Requires UKOddsApi Pro plan for player props access.
Sign up at https://ukoddsapi.com
Usage:
pip install requests
python player_props_scanner.py
"""
import requests
from datetime import date
# ─── Configuration ───────────────────────────────────────
API_KEY = "your_api_key_here"
BASE_URL = "https://api.ukoddsapi.com"
LEAGUE = "premier-league"
MARKET_FILTER = None # None = all props, or "goalscorer", "shots", "card"
MIN_BOOKMAKERS = 5 # Minimum bookmakers pricing a selection
# ─────────────────────────────────────────────────────────
headers = {"X-Api-Key": API_KEY}
def get_player_props(event_id, market_filter=None):
params = {"package": "full", "odds_format": "decimal"}
if market_filter:
params["market"] = market_filter
r = requests.get(
f"{BASE_URL}/v1/football/events/{event_id}/odds",
headers=headers, params=params
)
return r.json()
def analyse_props(odds_data, min_bookmakers=5):
results = []
for market in odds_data["markets"]:
# Group by selection
selections = {}
for sel in market["selections"]:
sname = sel["selection_name"]
if sname not in selections:
selections[sname] = []
selections[sname].append({
"bookmaker": sel["bookmaker_name"],
"odds": sel["odds"],
})
for sname, bookies in selections.items():
if len(bookies) < min_bookmakers:
continue
bookies.sort(key=lambda x: -x["odds"])
odds_values = [b["odds"] for b in bookies]
avg = sum(odds_values) / len(odds_values)
results.append({
"market": market["market_name"],
"group": market["market_group"],
"selection": sname,
"best_odds": bookies[0]["odds"],
"best_bookie": bookies[0]["bookmaker"],
"worst_odds": bookies[-1]["odds"],
"worst_bookie": bookies[-1]["bookmaker"],
"avg_odds": round(avg, 2),
"spread": round(((bookies[0]["odds"] / bookies[-1]["odds"]) - 1) * 100, 1),
"bookmaker_count": len(bookies),
"all_bookmakers": bookies,
})
return sorted(results, key=lambda x: -x["spread"])
def main():
r = requests.get(f"{BASE_URL}/v1/auth/verify", headers=headers)
if not r.json().get("ok"):
raise SystemExit("❌ Invalid API key. Get yours at ukoddsapi.com (Pro plan required)")
print("=" * 70)
print("🎯 UK PLAYER PROPS SCANNER")
print(f" Comparing props across 25+ UK bookmakers")
print(f" League: {LEAGUE}")
print("=" * 70)
fixtures = requests.get(
f"{BASE_URL}/v1/football/events",
headers=headers,
params={"league": LEAGUE, "schedule_date": date.today().isoformat()}
).json().get("events", [])
if not fixtures:
fixtures = requests.get(
f"{BASE_URL}/v1/football/events",
headers=headers,
params={"league": LEAGUE}
).json().get("events", [])
print(f"\n📅 {len(fixtures)} fixtures to scan\n")
for fixture in fixtures[:5]:
print(f"\n{'='*70}")
print(f"⚽ {fixture['event_title']}")
print(f"{'='*70}")
odds = get_player_props(fixture["event_id"], MARKET_FILTER)
results = analyse_props(odds, MIN_BOOKMAKERS)
if not results:
print(" No player props available yet for this fixture")
continue
# Group by market type
by_market = {}
for r in results:
if r["market"] not in by_market:
by_market[r["market"]] = []
by_market[r["market"]].append(r)
for market_name, props in by_market.items():
print(f"\n 📈 {market_name}")
print(f" {'Player/Selection':<25}{'Best':>7}{'Bookie':<16}{'Worst':>7}{'Spread':>8}{'#':>4}")
print(f" {'-'*67}")
for prop in props[:8]:
print(
f" {prop['selection'][:24]:<25}"
f"{prop['best_odds']:>7.2f}"
f"{prop['best_bookie'][:15]:<16}"
f"{prop['worst_odds']:>7.2f}"
f"{prop['spread']:>+7.1f}%"
f"{prop['bookmaker_count']:>4}"
)
print(f"\n{'='*70}")
print("💡 Higher spread = bigger value opportunity")
print(" Pro tip: back the best odds, lay at Betfair for risk-free value")
if __name__ == "__main__":
main()
Why this matters
A 15% spread on an anytime goalscorer market means one bookmaker is offering £2.80 while another offers £2.44 on the same player in the same match. If you're placing a £50 bet, that's the difference between a £140 return and a £122 return — £18 on a single bet, just by checking the right bookmaker.
Multiply that across every match, every player, every market type — and you see why comparing player props across all UK bookmakers is so valuable. Nobody else can build this tool because nobody else has UK player prop odds from more than one or two bookmakers.
API endpoints used
| Endpoint | Purpose |
|---|---|
GET /v1/football/events/{id}/odds?package=full |
Player and team props |
GET /v1/football/markets?package=full |
Discover available prop types |
GET /v1/football/events |
Find fixtures |
Use the market parameter to filter by type: goalscorer, shots, card, corner, booking.
UKOddsApi is the only API providing player prop odds across all 25+ UK bookmakers. Compare goalscorer, shots, cards, and assists prices in one call. Pro plan required. Get started at ukoddsapi.com