# DraftKings API Python: odds, Pick6 and DFS recipes

DraftKings data in Python: load odds, Pick6 lines, line history, DFS salaries and box scores into pandas. Copy a recipe and run it with a free API key.

Canonical page: https://draftkingsapi.com/draftkings-api-python. Markdown: https://draftkingsapi.com/draftkings-api-python.md. Site index for agents: https://draftkingsapi.com/llms.txt.

## Free API key
- **Free plan:** 50 requests a month at 20 a minute, every dataset, no card, no email verification.
- **Sign up:** https://draftkingsapi.com/auth/sign-up?ref=ai-claudebot
- **From inside an agent session:** POST https://draftkingsapi.com/api/v1/agent/signup with JSON `{"data_wanted": "...", "use_case": "...", "ref": "ai-claudebot"}`, show your user the approval_url, then poll https://draftkingsapi.com/api/v1/agent/token with device_code. The key is returned once.
- **No key yet:** `curl --fail-with-body "https://draftkingsapi.com/api/v1/demo/pick6"` returns labeled sample rows (sample:true) in the real response shape.

## Quickstart

1. Create a free key at https://draftkingsapi.com/auth/sign-up?ref=ai-claudebot (no card) and export it: `export DRAFTKINGS_API_KEY=...`

2. Call the API with curl:

```bash
curl --max-time 30 --fail-with-body 'https://draftkingsapi.com/api/v1/pick6/projections?sport=NBA&limit=5' \
  -H "X-User-Request: Build a prop screen from current Pick6 lines" \
  -H "Authorization: Bearer $DRAFTKINGS_API_KEY"
```

3. Or call it from Python:

```python
import os, requests

response = requests.get(
    "https://draftkingsapi.com/api/v1/pick6/projections?sport=NBA&limit=5",
    headers={
        "Authorization": "Bearer " + os.environ["DRAFTKINGS_API_KEY"],
        "X-User-Request": "Build a prop screen from current Pick6 lines",
    },
    timeout=30,
)
if not response.ok:
    raise RuntimeError(f"API {response.status_code}: {response.text}")
print(response.json())
```

4. Full walkthrough: https://draftkingsapi.com/quickstart. OpenAPI: https://draftkingsapi.com/openapi.json. Every route accepts format=csv on list endpoints.

## Use it from Claude (MCP)

MCP server: https://draftkingsapi.com/api/mcp (Streamable HTTP). search, fetch, start_signup and finish_signup need no key; data tools use the same API key.

```bash
claude mcp add --transport http --scope user draftkings https://draftkingsapi.com/api/mcp --header "Authorization: Bearer $DRAFTKINGS_API_KEY"
```

Without a key yet: `claude mcp add --transport http draftkings https://draftkingsapi.com/api/mcp`, then call start_signup. Setup for other clients: https://draftkingsapi.com/agents.md.

## Pricing

| Plan | Price | Requests per UTC month | Requests per minute | Pick6 stream | Signed webhooks |
| --- | --- | --- | --- | --- | --- |
| Free | $0/month | 50 | 20 | No | No |
| Developer | $49/month | 10,000 | 60 | Yes | No |
| Production | $149/month | 50,000 | 120 | Yes | Yes |

- **Free:** Build and test against every dataset.
- **Developer:** Keep your app running with more requests and push delivery.
- **Production:** Serve real users with the largest allowance and push delivery.

Plans and checkout: https://draftkingsapi.com/pricing

## From a small client to a DataFrame

Build a prop screen, a salary explorer or a research notebook in a few lines. There is no package to install: this client is the Python standard library, and every recipe below builds on it. Save it as `dk.py`, or paste it at the top of a notebook.

```
export DRAFTKINGS_API_KEY=your_key
curl --fail-with-body "https://draftkingsapi.com/api/v1/coverage" \
  -H "X-User-Request: Check which DraftKings datasets fit a new app" -H "Authorization: Bearer $DRAFTKINGS_API_KEY"
```

```
import json, os, urllib.parse, urllib.request

BASE = os.environ.get("DRAFTKINGS_API_BASE", "https://draftkingsapi.com")
KEY = os.environ["DRAFTKINGS_API_KEY"]


def get(path, params):
    url = f"{BASE}{path}?{urllib.parse.urlencode(params)}"
    request = urllib.request.Request(url, headers={"Authorization": f"Bearer {KEY}", "X-User-Request": "Check which DraftKings datasets fit a new app"})
    with urllib.request.urlopen(request, timeout=30) as response:
        return json.load(response)


def rows(path, params, max_pages=3):
    """Follow next_cursor, and stop at a page budget so a run cannot spend your quota."""
    params = dict(params)
    for _ in range(max_pages):
        page = get(path, params)
        yield from page["data"]
        if not page["pagination"]["has_more"]:
            return
        params["cursor"] = page["pagination"]["next_cursor"]
```

This client keeps things short. For retries on a 503 and handling for each status, use the client in [Python and cURL](/guides/curl-python).

## Odds into pandas

One row per side, per market, per game. Pivot it to get one row per game.

```
import pandas as pd

odds = pd.DataFrame(rows("/api/v1/odds", {"sport": "NBA", "status": "pre", "view": "compact", "limit": 1000}))
odds["line"] = pd.to_numeric(odds["line"])  # A numeric string, or empty on a moneyline.
print(odds.pivot_table(index="event_id", columns=["market", "side"], values="american", aggfunc="first"))
```

## Current Pick6 lines

Pick6 rows come as ladders, with several targets for one player and stat. `is_default` marks the main line, which is usually the one you want on a screen. Each row has its capture time in `observed_at`.

```
lines = pd.DataFrame(rows("/api/v1/pick6/projections", {"sport": "NFL", "stat": "Receiving Yards", "status": "open", "view": "compact", "limit": 1000}))
main = lines[lines["is_default"] == True]  # One main target per player and stat, the rest are ladder rungs.
print(main[["player", "team", "opponent", "start_time", "target_value", "observed_at"]].sort_values("target_value", ascending=False))
```

Rows also carry `more_multiplier` and `less_multiplier`. Keep them as their own columns. A standings multiplier is not decimal odds, a probability or a payout, so never feed one into an odds formula.

## How one prop moved

`/api/v1/pick6/line-history` returns the recorded changes for a prop and answers the first question you have: did the line itself move, or only a multiplier? Take `pickable_id` from a row above.

```
page = get("/api/v1/pick6/line-history", {"pickable_id": "PICKABLE_ID_FROM_A_ROW", "limit": 100})
print(page["prop"]["stats"], page["prop"]["first_observed_at"], page["prop"]["last_observed_at"])
for change in page["data"]:
    if change["line_moved"]:
        print(change["observed_at"], change["player"], change["stat"],
              change["previous"]["target_value"], "to", change["current"]["target_value"])
```

## DFS salaries by slate

One row per roster slot, so one player can appear several times with different salaries. That is the data, so do not deduplicate it by name.

```
salaries = pd.DataFrame(rows("/api/v1/dfs/players", {"sport": "NFL", "view": "compact", "limit": 1000}))
print(salaries.groupby(["slate_id", "position"])["salary"].describe())
```

## Box scores

ESPN box scores for NBA, NFL and NHL come as a list of stats per player-game. Read `numeric`, which is null for compound values such as 4-9.

```
def game_stat(row, key):
    for stat in row["data"].get("stats", []):
        if stat["key"] == key and stat["scope"] == "game":
            return stat["numeric"]  # None for compound values such as 4-9, and never a guess.
    return None


page = get("/api/v1/stats/player-games", {"sport": "NBA", "season": 2027, "completed": "true", "limit": 200})
for row in page["data"]:
    print(row["data"]["name"], game_stat(row, "points"), row["game"]["event_id"])
# Next page: pass page["pagination"]["next"] as "after". Statistics use after, not cursor.
```

## Spending your allowance well

Free includes 50 requests/month at 20/minute; Developer is $49/month with 10,000 requests/month at 60/minute; Production is $149/month with 50,000 requests/month at 120/minute. A repeat call answered with 304 Not Modified is free when you send `If-None-Match`, and the page budget in `rows` stops a loop from running away.

```
curl -O https://draftkingsapi.com/examples/draftkings-quickstart.py
python3 draftkings-quickstart.py
```

## FAQ

### Is there a DraftKings Python package?
None is needed. The API is HTTPS and JSON, and the client on this page uses only the standard library. pandas is optional and used here to shape results.

### What does a request cost?
One page of rows is one request, including a CSV page. A page with view=compact can hold up to 1,000 rows, so it is the cheapest way to read a full board.

### Can I use this in a Jupyter notebook?
Yes. Paste the client into the first cell, or download the quickstart notebook from the examples page. Set the key in your environment rather than in a cell, so it never lands in a saved notebook.

### Is there a free DraftKings API?
Yes. A free key needs no card and includes 50 requests a month at 20 a minute, across every dataset. To see the response shape first, /api/v1/demo/pick6 and /api/v1/demo/dfs return labeled sample rows with no key.

### Is this an official DraftKings API?
Independent service operated by DD Inc., not affiliated with or endorsed by DraftKings. No wagering: this service does not place, recommend or settle entries or bets.

### Can I use it from an AI agent?
Yes. Connect the MCP server at https://draftkingsapi.com/api/mcp, or call REST with a Bearer key. An agent can get a key for its user with start_signup.

### How do I reach support?
Email support@draftkingsapi.com.

Independent service operated by DD Inc., not affiliated with or endorsed by DraftKings. No wagering: this service does not place, recommend or settle entries or bets.
