draftkings apiDashboard ↗
DOCUMENTATION / QUICKSTART

Your first request.

Observed Pick6 props and separate DFS datasets. Query the stored observations, inspect the clocks, and build from there.

01 / Get connected

Create an account, open your dashboard and create a named API key. Save the secret immediately—it is shown once. Keep it on your server.

No key yet? curl https://draftkingsapi.com/api/v1/demo/pick6 or /api/v1/demo/dfs return invented sample rows, labeled sample: true, in the real response shape. They need no key and spend no quota. See the one-minute quickstart.

# Example request. Replace the host with your running instance.
curl 'https://YOUR_HOST/api/v1/coverage' \
  -H "Authorization: Bearer $DRAFTKINGS_API_KEY"
# Then choose a sport with fresh supported observations.
curl 'https://YOUR_HOST/api/v1/pick6/projections?sport=NFL&limit=5' \
  -H "Authorization: Bearer $DRAFTKINGS_API_KEY"

Use the playground for a real call with your own key. There is no need to install an SDK.

02 / Choose a dataset

GET/api/v1/demo/pick6Keyless sample rows. Invented, not data
GET/api/v1/demo/dfsKeyless sample rows. Invented, not data
GET/api/v1/pick6/projectionsStored Pick6 market rows and selections
GET/api/v1/projectionsAlias for Pick6 projections
GET/api/v1/pick6/historyActual changed observations only
GET/api/v1/changesAlias for Pick6 change history
GET/api/v1/dfs/sportsCaptured US-DK sport catalog
GET/api/v1/dfs/contestsObserved multi-sport contests
GET/api/v1/dfs/slatesObserved multi-sport draft groups
GET/api/v1/dfs/playersDraftables for collected slates
GET/api/v1/eventsObserved sportsbook events and game state
GET/api/v1/sportsbook/scopesActual native subscribed scope IDs, readiness and retained counts
GET/api/v1/sportsbook/snapshotAtomic scoped native snapshot and independent cursor; all valid plans
GET/api/v1/sportsbook/wsPaid server-side native WebSocket upgrade; separate from Pick6 SSE
GET/api/v1/oddsDraftKings mainline prices via ESPN
GET/api/v1/historyRecorded sportsbook observations and changes
GET/api/v1/coverageMeasured fresh/retained rows and captured/discovered scopes
GET/api/v1/stats/gamesIndependent ESPN game snapshots; sport and season required
GET/api/v1/stats/player-gamesIndependent player/category statistics with game evidence
GET/api/v1/stats/team-gamesIndependent team statistics with game evidence

All /api/v1 endpoints require a Bearer key. /api/health and /api/openapi are public.

03 / Filter, then paginate

For the existing Pick6, DFS, sportsbook and context pages, limit defaults to 25 (1–100). offset defaults to 0 (0–10,000). Pick6 supports sport filters such as sport=NFL or sport=NHL, stat=Points, pick_group_id, pickable_id, and pickable_market_id. The three identity fields identify one market row together.

For changed observations, use after=CHANGE_ID and the returned pagination.next_after to continue. Use offset 0 when advancing after. First captures establish a baseline; unchanged captures and live-stat ticks do not manufacture offer changes. Empty history is valid until an actual offer change is observed.

Append format=csv to Pick6 endpoints to export up to 100 matching rows per page. Selections and entities remain JSON cells so multiple sides, multipliers and entity joins stay intact.

DFS contests, slates and players support sport=NBA and slate_id; the sports catalog supports pagination only. One athlete can have multiple roster-slot draftables. Sportsbook events accept sport and event_id. Odds additionally accept market. History accepts sport,event_id, market and offer_id. Coverage accepts no query parameters. Unsupported or duplicate parameters and invalid bounds return 400.

Independent statistics use cursor pagination

/api/v1/stats/games, /api/v1/stats/player-games and /api/v1/stats/team-games require sport=NBA|NFL|NHL and season. NFL uses its starting season year; NBA/NHL use their ending year. Optional filters: event_id, completed=true|false, from, to, limit (default 50,1–200) and after from pagination.next. Player games additionally support athlete_id and team_id; team games support team_id. IDs are numeric source IDs of 1–20 digits. Dates are ISO8601 offset timestamps or YYYY-MM-DD UTC-midnight boundaries, with from no later than to.

Statistics are JSON only. Offset and CSV filters are rejected. Responses contain available, data rows wrapping their payload under data, optional game context for player/team rows, and pagination.next. Empty matching pages explicitly return available=false. Missing statistics schema/storage returns 503. Pagination is not a frozen export.

curl 'https://YOUR_HOST/api/v1/stats/player-games?sport=NFL&season=2025&limit=5' -H "Authorization: Bearer $DRAFTKINGS_API_KEY"

Check collection coverage and returned evidence before using these samples; the coverage endpoint is not a full-season statistics inventory. Only limited independently captured 2025 NFL and 2025–26 NBA/NHL games are initially retained; a valid season filter is not a full-season availability promise. Original observations were captured later than those games. They do not establish knowledge available at the historical game date, as-of correction history or native sportsbook player props.

// Illustrative shape — NOT a current source record
{
  "dataset": "pick6",
  "data": [{
    "pick_group_id": "EXAMPLE_GROUP",
    "pickable_id": "EXAMPLE_PICKABLE",
    "pickable_market_id": "EXAMPLE_MARKET",
    "stat": "Points", "target_value": 20.5,
    "period": null, "is_paused": false,
    "selections": [{"side": "More", "standings_multiplier": 1.1}],
    "observed_at": "CAPTURE_TIME", "source_updated_at": null,
    "age_seconds": 1200, "stale": true
  }],
  "pagination": {"limit": 10, "offset": 0, "has_more": true}
}

Capture age is measured per row. Projections preserve previously observed rows absent from a partial response. They are not a guarantee of current selectability. Pagination is bounded but does not freeze changing captures across requests.

04 / Handle responses

400 invalid input · 401 invalid or revoked key · 403 rejected origin or unavailable redistribution · 409 key/capture conflict · 429 account quota exceeded · 503 storage or dataset unavailable.

New accounts receive 500 requests per UTC calendar month and 20 per UTC minute on Free. Developer includes 10,000/month and 60/minute; Production includes 50,000/month and 120/minute. Existing legacy accounts keep their daily terms. Limits apply across all keys. Valid authenticated requests count even if no rows match or a dataset is unavailable. Invalid queries authenticated before validation also count. Responses use no-store. For 429, wait for the next UTC minute, day or calendar-month boundary for your account contract.

{"error":{"code":503,"message":"Sportsbook observations unavailable. Direct source access restricted; licensed ingestion pending."}}

05 / Keep the meaning intact

Pick6: selected multi-sport/category snapshots are covered. Market rows include alternatives and paused/live entries—not unique athletes or a complete board. Every explicit source selection is retained, even when one-sided. Standings multipliers are not decimal odds or probabilities. Missing side/period/promo labels remain unknown. A live-stat clock is not the offer clock; source market update time is unknown.

Paused entries are explicitly marked and must not be treated as selectable. Disappearance from a partial category is not settlement. Data is flagged stale after 15 minutes without a fresh observation. Worker targets are five minutes for sportsbook/Pick6 and fifteen minutes for DFS when enabled; actual last successes appear on /status.

DFS: salaries and FPPG are not sportsbook lines. Contests and slates are public-source observations, not a promise a contest is open. Sportsbook: DraftKings-attributed ESPN moneyline, spread and total observations across eight leagues. The event_statusfield is pre, in, post or unknown; completed games are historical quotations. Source price update time remains null. Worker collection is bounded; sportsbook player props, alternate lines and futures are not supplied by this adapter. American prices and history remain separate.

Technical access is not a redistribution license. Review source context and actual capture times →

Native sportsbook: snapshot, then WebSocket

Native DraftKings subscribed scopes are separate from the ESPN-observed mainlines and the Pick6 SSE stream. Discover actual region/site/league/subcategory IDs with GET /api/v1/sportsbook/scopes. Scope discovery and GET /api/v1/sportsbook/snapshot?scope=... accept valid keys on every plan, including Free, under normal request quotas. These operator-gated routes return 503 until the feature and an authoritative healthy baseline are available.

The snapshot returns events, markets, selections and an atomic decimal string cursor. Save state and cursor together. Snapshots are bounded at 10,000 entities / 8 MiB and fail explicitly on overflow. Coverage is limited to the returned scope; an NFL scope is not global sportsbook or broader player-prop coverage. NOT_STARTED events are pregame.

// Server-side Node.js; npm install ws. Keep the API key in your environment.
import WebSocket from 'ws';
const headers = {Authorization: 'Bearer ' + process.env.DRAFTKINGS_API_KEY};
const scope = process.env.DRAFTKINGS_NATIVE_SCOPE; // from /api/v1/sportsbook/scopes
const url = new URL('https://draftkingsapi.com/api/v1/sportsbook/snapshot');
url.searchParams.set('scope', scope);
const response = await fetch(url, {headers, redirect: 'error'});
if (!response.ok) throw new Error('Snapshot unavailable: ' + response.status);
const snapshot = await response.json();
// Atomically persist snapshot.state and snapshot.cursor before connecting.
url.pathname = '/api/v1/sportsbook/ws';
url.protocol = 'wss:';
url.searchParams.set('after', snapshot.cursor);
const socket = new WebSocket(url, {headers, followRedirects: false});
socket.on('message', bytes => {
  const frame = JSON.parse(bytes.toString());
  // Serialize durable apply + cursor commits. Deduplicate by string cursor.
  // reconnect / ordinary network close: resume from your last durable cursor.
  // resync_required: take a fresh atomic snapshot before reconnecting.
});

WebSocket access requires a matching monthly Developer or Production subscription. Each upgrade costs one request and rotates after 280 seconds. Developer allows two concurrent connections; Production five. Browser WebSocket constructors cannot set a Bearer header: use a trusted server client, never a key in a URL, subprotocol or public bundle.

sportsbook.change and payload-free checkpoint frames carry native cursors. A resync_required frame, source rebaseline, stale transport, replay gap or slow receiver requires a fresh snapshot. Normal rotation emits a reconnect frame; resume from your last durable cursor without another snapshot. After a customer-network disconnect you can try that same durable cursor; the server forces resnapshot if replay is invalid. The 262 KiB event/buffer bound fails closed. The native cursor is separate from Pick6 and does not recover provider updates missed while disconnected. No full-book, uninterrupted recovery or latency SLA is implied. Check actual readiness and retained counts →

Resources

Starter kit, response schema, pagination, errors and limits, webhooks, multipliers, line movement study, history sample, hit rates, agent setup and OpenAPI.

06 / Durable customer delivery

Developer and Production monthly plans include resumable Pick6 change streams. Production also includes signed webhooks. Delivery follows the changes we capture; check dataset coverage before building around it.

Start with a snapshot

First GET /api/v1/stream/snapshot?sport=NFL with your Bearer key. The optional sport filter bounds a retained snapshot to 10,000 rows or 2 MB; larger results return 413. Save its string snapshot_cursor, then connect /api/v1/stream with Last-Event-ID or after. Without a cursor, explicitly choose start=latest to receive new changes without a baseline; otherwise 428 is returned. The snapshot and cursor share one atomic source-table read.

SSE sends pick6.change and checkpoint events with durable IDs. Each connection lasts at most 55 seconds and costs one metered request, as does bootstrap. Persist IDs and reconnect. The stream is not sport-filtered; apply your snapshot's sport filter to incoming changes. Purged cursors return 410; ahead cursors return 409. An oversized event produces delivery.error without advancing its ID. Snapshots can include stale retained rows.

Manage webhooks in your dashboard

Create a receiver, copy its signing secret once, and inspect delivery attempts in your dashboard. You can revoke a receiver or replay retained changes there.

Use the signed-in dashboard cookie session for /api/account/webhooks, not an API key. GET lists owned hooks and recent delivery diagnostics. POST {"url":"https://YOUR_RECEIVER/path"} returns an ID and one-time signing secret; maximum five active hooks. Mutations require the canonical Origin. DELETE an owned ID to revoke; PATCH {"id":"HOOK_ID","after":"CURSOR","through":"CURSOR"} requeues at most 100 retained source changes for an active owned hook. No secret is returned by later reads.

Verify every callback

Verify x-dk-signature as HMAC-SHA256 of the timestamp, a dot and the exact raw body, using constant-time comparison and a clock window. Deduplicate x-dk-event-id; delivery is at-least-once. Retries preserve event ID/body. Redirects fail; 403 disables only that hook, 429 sets a finite hook-local cooldown. Other failures use bounded backoff and terminal attempts. Replay never erases attempt history.

No automatic deletion or unlimited replay window is promised. Storage pressure pauses writes and dispatch, preserving pending and terminal evidence. Source purge requires a new snapshot for stale cursors. Customer delivery is independent from Stripe billing webhooks and does not provide sportsbook player props or grading.