FanLine Wire API documentation
FanDuel Sportsbook odds, pre-match and live. Every sport FanDuel lists, with moneyline, spread and totals. For NFL, college football, NBA and men's college basketball, every market on every game: halves, quarters, team totals and player props. Pick what you want over REST, then stream changes over a WebSocket. Every selection comes with a ready-made FanDuel bet-slip link.
Overview
Base URL: https://api.fanlinewire.com. Send your key as the x-api-key header, as Authorization: Bearer <key>, or as ?key=.
| Endpoint | What it gives you | Plans |
|---|---|---|
GET /v1/board.json | All sports: every FanDuel market with current prices (moneyline, spread, totals, futures). | All |
GET /v1/markets | All sports: the same list without prices. | All |
GET /v1/{state}/events | Every NFL, NCAAF, NBA and NCAAB game in that state: event id, teams, start time, market count, live flag. | All |
GET /v1/{state}/markets | That state's full board with current prices, filtered by league, game, market category and period. | All |
wss://api.fanlinewire.com/v1/stream | Subscribe to a filter; get a snapshot, then every change to those markets. | Full |
GET /v1/me | Your plan and limits. Not metered. | All |
{state} is nj (New Jersey) or ks (Kansas) — see States. REST limits: Free 100/day, Basic 1,000/day, Full 30/sec. See pricing.
Quickstart
Every first-half spread for this week's NFL games in New Jersey, with the bet-slip link:
import json, urllib.request
KEY = "YOUR_KEY"
url = "https://api.fanlinewire.com/v1/nj/markets?league=nfl&period=1h&category=spread"
req = urllib.request.Request(url, headers={"x-api-key": KEY})
for m in json.load(urllib.request.urlopen(req))["markets"]:
for s in m["selections"]:
print(m["event"], "|", m["market_name"], "|", s["name"], s["handicap"], s["american"], "|", s["betslip_url"])
All sports
GET /v1/board.json
Every market FanDuel lists across all sports (soccer, tennis, hockey, baseball, golf, MMA and more), with moneyline, spread and totals plus futures and outrights. Each row has market_id, sport, fixture, market, inplay, updated_at and prices[] of {selection_id, american, decimal}. GET /v1/markets returns the same list without prices. Read from FanDuel New Jersey.
Events (NFL, NCAAF, NBA, NCAAB)
GET /v1/nj/events?league=nfl
league is optional: nfl, ncaaf, nba or ncaab.
{
"ok": true, "state": "nj", "sequence": 18342,
"events": [{
"event_id": "36160506", "league": "nfl",
"name": "Seattle Seahawks @ Denver Broncos",
"away_team": "Seattle Seahawks", "home_team": "Denver Broncos",
"start_time": "2026-10-16T00:16:00.000Z",
"market_count": 127, "inplay": false
}]
}
Markets (NFL, NCAAF, NBA, NCAAB)
GET /v1/nj/markets?league=nfl&event_id=36160506&category=spread,total,team_total&period=game,1h,1q
All filters are optional and combine. Comma-separate multiple values.
| Filter | Values |
|---|---|
league | nfl, ncaaf, nba, ncaab |
event_id | Ids from /v1/{state}/events (up to 500) |
category | moneyline, spread, total, team_total, alt_spread, alt_total, player_prop, other |
period | game, 1h, 2h, 1q, 2q, 3q, 4q |
market_id | Specific market ids (up to 500) |
{
"ok": true, "state": "nj", "sequence": 18342,
"markets": [{
"market_id": "734.191144271", "event_id": "36160506", "league": "nfl",
"event": "Seattle Seahawks @ Denver Broncos",
"away_team": "Seattle Seahawks", "home_team": "Denver Broncos",
"start_time": "2026-10-16T00:16:00.000Z",
"market_name": "1st Half Spread", "market_type": "FIRST_HALF_HANDICAP",
"period": "1h", "category": "spread",
"status": "OPEN", "inplay": false, "updated_at": "2026-10-14T07:19:23.378Z",
"selections": [{
"selection_id": "50213", "name": "Seattle Seahawks",
"handicap": -1.5, "status": "ACTIVE", "american": 105, "decimal": 2.05,
"betslip_url": "https://nj.sportsbook.fanduel.com/addToBetslip?marketId=734.191144271&selectionId=50213"
}]
}]
}
market_type is FanDuel's own type code, for finer filtering in your code (for example PLAYER_X_RECEPTIONS_MEDIUM). Player props carry the player and stat in market_name (for example AJ Barner - Total Receptions). american is null when a selection is unpriced.
Live WebSocket (Full plan)
wss://api.fanlinewire.com/v1/stream?key=YOUR_KEY
On connect you get a hello. Then send one subscribe message: a state (nj or ks) plus the same filters as /v1/{state}/markets, as JSON:
{"action": "subscribe", "state": "nj", "league": "nfl",
"category": ["moneyline", "spread", "total", "team_total"],
"period": ["game", "1h", "1q"]}
You receive, in order:
| Frame | Meaning |
|---|---|
snapshot | Current state of every matching market, in chunks (chunk of chunks). Same market shape as REST. |
subscribed | Snapshot done. Carries the market count and sequence. |
update | Markets that changed: price, line, market or selection status. Full market object, so just replace what you hold. |
snapshot with reason: "resync" | The feed restarted; replace your state with this. |
error | A bad message or filter, with a plain-English message. |
Send another subscribe at any time to change your filter (a new snapshot follows). Send {"action":"unsubscribe"} to stop. Answer WebSocket pings (every client library does this for you). Up to 3 connections per key.
import asyncio, json, websockets
async def main():
async with websockets.connect("wss://api.fanlinewire.com/v1/stream?key=YOUR_KEY") as ws:
await ws.send(json.dumps({"action": "subscribe", "state": "ks", "league": "nba",
"category": ["spread", "total", "player_prop"]}))
book = {}
async for raw in ws:
msg = json.loads(raw)
if msg["type"] in ("snapshot", "update"):
for m in msg["markets"]:
book[m["market_id"]] = m
if msg["type"] == "update":
for m in msg["markets"]:
print(m["event"], m["market_name"], [(s["name"], s["handicap"], s["american"]) for s in m["selections"]])
asyncio.run(main())
Bet-slip links
Every selection on a state board carries betslip_url, for example https://nj.sportsbook.fanduel.com/addToBetslip?marketId=…&selectionId=…. Opening it puts that selection on the FanDuel bet slip in that state. It works for full-game lines, halves, quarters, team totals and player props. Placing the bet happens in your own FanDuel account.
Market status
status is OPEN or SUSPENDED and flips in real time. A suspension usually means the book pulled the market (a big play, a review, a line reset).
Update pace
FanDuel pushes price changes to us as they happen. We forward each one to your stream as soon as it reaches us, typically within about 0.2 seconds. The game list and new markets (new props, new games) are refreshed every 10 minutes. Quiet periods are normal.
States
FanDuel gives each state its own market ids and bet-slip links, so each state is its own board. Use the one you bet in.
| State | state | Full board (NFL, NCAAF, NBA, NCAAB) |
|---|---|---|
| New Jersey | nj | Live |
| Kansas | ks | Live |
The all-sports endpoints (/v1/board.json, /v1/markets) and the free odds.json demo read FanDuel New Jersey. Need another FanDuel state? Email hello at fanlinewire dot com and we'll add it.
Coverage: every game on FanDuel's NFL, NCAAF, NBA and men's college basketball (NCAAB) pages, and every market on each game: moneyline, spread, total, team totals, alternate lines, halves, quarters and player props. Coverage follows what FanDuel is offering; NBA player props, for example, appear as FanDuel posts them, usually closer to tip-off. The game list and new markets refresh every 10 minutes; prices push as they change.