Python example

FanDuel API in Python

Four short steps with requests: authenticate, list FanDuel Sportsbook player props, read one prop's move history, and poll for whatever moved. A free key covers building and testing; move history and real-time polling need the Developer plan.

FanDuel API is an independent data service operated by FF API Inc. It is not affiliated with, endorsed by or sponsored by FanDuel.

1. Install and authenticate

Shell
pip install requests
export FANDUEL_API_KEY="fd_..."

Get a key from the dashboard after you sign up. The helper below sends it as a Bearer token, returns the data list from each response, and waits when it hits a rate limit (HTTP 429).

props.py
import os
import time
from datetime import datetime, timezone

import requests

BASE = "https://fanduelapi.com/api/v1"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['FANDUEL_API_KEY']}"


def get(path, **params):
    """GET one endpoint and return its data list, waiting out rate limits."""
    while True:
        r = SESSION.get(f"{BASE}{path}", params=params, timeout=10)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        r.raise_for_status()
        return r.json()["data"]


def price(odds):
    """American odds as text. A suspended market can come back with no price."""
    return "off" if odds is None else f"{odds:+d}"

2. List props

Current NBA points props
props = get("/props", sport="nba", stat="points")
for p in props:
    print(f"{p['player']:<26} {str(p['line']):>5}  over {price(p['over_american']):>5}  under {price(p['under_american']):>5}")
Output with the sample slate
Luka Dončić                 33.5  over  -105  under  -115
Shai Gilgeous-Alexander     31.5  over  -120  under  -102
Giannis Antetokounmpo       29.5  over  -114  under  -106

A suspended market can return no price, which the price helper prints as off. Filter by sport, league, event_id, player or stat. Results are paginated; the parameters are in /openapi.json.

3. Read a move history (Developer)

Move history is part of the Developer and Production plans. On a free key, skip this step.

Where one prop opened and how it moved
# Move history is part of the Developer and Production plans.
history = sorted(get(f"/props/{props[0]['id']}/history"), key=lambda m: m["observed_at"])
opened, now = history[0], history[-1]
print(f"{props[0]['player']}: opened {opened['line']} at {opened['observed_at']}, "
      f"now {now['line']}, {len(history) - 1} moves")
Sample history response, not live data
{
  "data": [
    {
      "observed_at": "2026-11-05T14:02:00Z",
      "line": 3.5,
      "over_american": 105,
      "under_american": -135,
      "over_decimal": 2.05,
      "under_decimal": 1.741
    },
    {
      "observed_at": "2026-11-05T16:44:00Z",
      "line": 3.5,
      "over_american": -102,
      "under_american": -120,
      "over_decimal": 1.98,
      "under_decimal": 1.833
    },
    {
      "observed_at": "2026-11-05T19:12:00Z",
      "line": 3.5,
      "over_american": -118,
      "under_american": -104,
      "over_decimal": 1.847,
      "under_decimal": 1.962
    },
    {
      "observed_at": "2026-11-05T22:20:00Z",
      "line": 2.5,
      "over_american": -165,
      "under_american": 130,
      "over_decimal": 1.606,
      "under_decimal": 2.3
    }
  ]
}

More on what a history holds is on the line movement page.

4. Poll for changes

Print every prop that moved
# Seconds between polls. One poll is one request, so pick the interval your plan's monthly quota allows:
#   Free: 28800
#   Developer: 300
#   Production: 60
#   Scale: 20
POLL_SECONDS = 28800


def utc_now():
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


since = utc_now()
while True:
    time.sleep(POLL_SECONDS)
    checked = utc_now()
    for p in get("/props", sport="nba", updated_since=since):
        print(f"{p['observed_at']}  {p['player']} {p['stat']} now {p['line']} "
              f"({price(p['over_american'])} / {price(p['under_american'])})")
    since = checked

The script defaults to the Free interval of 28800 seconds, about 480 minutes, so a free key lasts the month. On Developer, set POLL_SECONDS to 300 for real-time polling. Asking only for props updated since your last check keeps each call small. The Developer plan adds the change feed at /api/v1/changes, and Production adds webhooks, so you can stop polling altogether.

Questions

Make your first call in a few minutes

Sign up, create a key in the dashboard, and send it as a Bearer token. The free plan needs no card.

Get a free API key