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=.

EndpointWhat it gives youPlans
GET /v1/board.jsonAll sports: every FanDuel market with current prices (moneyline, spread, totals, futures).All
GET /v1/marketsAll sports: the same list without prices.All
GET /v1/{state}/eventsEvery NFL, NCAAF, NBA and NCAAB game in that state: event id, teams, start time, market count, live flag.All
GET /v1/{state}/marketsThat state's full board with current prices, filtered by league, game, market category and period.All
wss://api.fanlinewire.com/v1/streamSubscribe to a filter; get a snapshot, then every change to those markets.Full
GET /v1/meYour 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.

FilterValues
leaguenfl, ncaaf, nba, ncaab
event_idIds from /v1/{state}/events (up to 500)
categorymoneyline, spread, total, team_total, alt_spread, alt_total, player_prop, other
periodgame, 1h, 2h, 1q, 2q, 3q, 4q
market_idSpecific 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:

FrameMeaning
snapshotCurrent state of every matching market, in chunks (chunk of chunks). Same market shape as REST.
subscribedSnapshot done. Carries the market count and sequence.
updateMarkets 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.
errorA 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.

StatestateFull board (NFL, NCAAF, NBA, NCAAB)
New JerseynjLive
KansasksLive

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.