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
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).
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
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}")
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 -106A 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.
# 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")
{
"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
# 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
Related
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