Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fieldfunded.com/llms.txt

Use this file to discover all available pages before exploring further.

Analyze Sports Odds Data with Python and Pandas

Turn raw odds data into actionable insights using Python and pandas. This tutorial fetches live odds from the FieldFunded API, loads them into DataFrames, and runs analysis — implied probabilities, margin calculation, and value detection.

Prerequisites

pip install requests pandas
You also need a free API key from fieldfunded.com/docs (no credit card required).

Step 1: Fetch Odds into a DataFrame

import requests
import pandas as pd

API_KEY = "your_api_key_here"
BASE = "https://api.fieldfunded.com/v1"
H = {"X-API-Key": API_KEY}

# Get Premier League events
events = requests.get(
    f"{BASE}/events",
    headers=H,
    params={"sport": "soccer", "league": "england_epl"}
).json()

# Build a flat list of all match winner odds
rows = []
for event in events["events"][:10]:
    odds = requests.get(
        f"{BASE}/events/{event['id']}/odds", headers=H
    ).json()

    for market in odds["markets"]:
        if "1x2" not in market["name"].lower() and \
           "winner" not in market["name"].lower():
            continue
        for sel in market["selections"]:
            rows.append({
                "match": f"{event['home_team']} vs {event['away_team']}",
                "kickoff": event["commence_time"],
                "selection": sel["name"],
                "odds": sel["odds"],
                "market_id": market["id"],
                "selection_id": sel["id"],
            })

df = pd.DataFrame(rows)
print(df.head(9))
Output:
                              match              kickoff    selection  odds
0  Manchester United vs Liverpool  2026-05-10T15:00:00Z   Man United  3.40
1  Manchester United vs Liverpool  2026-05-10T15:00:00Z         Draw  3.60
2  Manchester United vs Liverpool  2026-05-10T15:00:00Z    Liverpool  2.10
3        Arsenal vs Aston Villa  2026-05-10T17:30:00Z      Arsenal  1.55
4        Arsenal vs Aston Villa  2026-05-10T17:30:00Z         Draw  4.20
5        Arsenal vs Aston Villa  2026-05-10T17:30:00Z  Aston Villa  5.80

Step 2: Calculate Implied Probabilities

# Implied probability = 1 / decimal_odds
df["implied_prob"] = (1 / df["odds"] * 100).round(1)

# Group by match to calculate the margin (overround)
def calc_margin(group):
    total_prob = group["implied_prob"].sum()
    group["margin"] = (total_prob - 100).round(1)
    return group

df = df.groupby("match", group_keys=False).apply(calc_margin)

print(df[["match", "selection", "odds", "implied_prob", "margin"]].head(6))
Output:
                              match    selection  odds  implied_prob  margin
0  Manchester United vs Liverpool   Man United  3.40          29.4     5.9
1  Manchester United vs Liverpool         Draw  3.60          27.8     5.9
2  Manchester United vs Liverpool    Liverpool  2.10          47.6     5.9
3        Arsenal vs Aston Villa      Arsenal  1.55          64.5     5.1
4        Arsenal vs Aston Villa         Draw  4.20          23.8     5.1
5        Arsenal vs Aston Villa  Aston Villa  5.80          17.2     5.1
The margin (5-6%) represents the bookmaker’s edge. Lower margins mean better odds for bettors.

Step 3: Find Value Bets

A “value bet” is when you believe the true probability is higher than the implied probability. Use your own model or estimates:
# Example: your model's probability estimates
your_model = {
    "Man United": 32.0,    # Your model says 32%, book says 29.4%
    "Draw": 26.0,          # Model says 26%, book says 27.8%
    "Liverpool": 42.0,     # Model says 42%, book says 47.6%
    "Arsenal": 68.0,       # Model says 68%, book says 64.5%
}

df["model_prob"] = df["selection"].map(your_model)
df["edge"] = (df["model_prob"] - df["implied_prob"]).round(1)

# Value bets: where your model gives higher probability than the book
value_bets = df[df["edge"] > 0].sort_values("edge", ascending=False)
print(value_bets[["match", "selection", "odds", "implied_prob",
                   "model_prob", "edge"]])
Output:
                              match    selection  odds  implied_prob  model_prob  edge
3        Arsenal vs Aston Villa      Arsenal  1.55          64.5        68.0   3.5
0  Manchester United vs Liverpool   Man United  3.40          29.4        32.0   2.6

Step 4: Track Odds Over Time

Build a historical dataset by polling at intervals:
import time
from datetime import datetime

def collect_snapshots(event_id, intervals=10, pause=60):
    """Collect odds snapshots over time."""
    snapshots = []

    for i in range(intervals):
        odds = requests.get(
            f"{BASE}/events/{event_id}/odds", headers=H
        ).json()

        timestamp = datetime.now().isoformat()

        for market in odds["markets"]:
            if "winner" not in market["name"].lower():
                continue
            for sel in market["selections"]:
                snapshots.append({
                    "timestamp": timestamp,
                    "selection": sel["name"],
                    "odds": sel["odds"],
                })

        print(f"Snapshot {i + 1}/{intervals} collected")
        if i < intervals - 1:
            time.sleep(pause)

    return pd.DataFrame(snapshots)

# Collect 10 snapshots, 1 minute apart
# history = collect_snapshots("event_abc123", intervals=10, pause=60)

Step 5: Visualize Odds Movement

import matplotlib.pyplot as plt

def plot_odds_history(history_df):
    """Plot odds movement over time for each selection."""
    fig, ax = plt.subplots(figsize=(10, 5))

    for name, group in history_df.groupby("selection"):
        ax.plot(
            range(len(group)),
            group["odds"].values,
            marker="o",
            label=name,
            linewidth=2,
        )

    ax.set_xlabel("Snapshot")
    ax.set_ylabel("Decimal Odds")
    ax.set_title("Odds Movement Over Time")
    ax.legend()
    ax.grid(True, alpha=0.3)
    plt.tight_layout()
    plt.savefig("odds_movement.png", dpi=150)
    print("Chart saved to odds_movement.png")

# plot_odds_history(history)

Step 6: Export for Further Analysis

# Save to CSV
df.to_csv("epl_odds.csv", index=False)
print(f"Saved {len(df)} rows to epl_odds.csv")

# Save to Excel with multiple sheets
with pd.ExcelWriter("odds_analysis.xlsx") as writer:
    df.to_excel(writer, sheet_name="All Odds", index=False)
    value_bets.to_excel(writer, sheet_name="Value Bets", index=False)
    print("Saved to odds_analysis.xlsx")

Rate Limit Math

Analysis typeRequestsFrequencyMonthlyPlan
One-off EPL analysis (10 matches)11Once11Free
Weekly analysis across 3 leagues904x/month360Free
Daily odds tracking (10 events, hourly)240Daily7,200Free
Full pipeline with historical collection1,000Daily30,000Starter ($29)
Data analysis is typically low-frequency — most workflows fit in the free tier.

Get Your Free API Key

Start analyzing in 5 minutes — 10,000 free requests/month

See Pricing

All plans compared side by side