# Use a sports odds API from Python

To get sports odds in Python, send a GET request to https://getoddsapi.com/api/v1/odds with your key in an Authorization: Bearer header, then read the data array. The standard library is enough; there is also a dependency-free GetOdds Python client, and the rows load straight into a pandas DataFrame with prices, lines and capture times.

Canonical page: https://getoddsapi.com/odds-api-python

**Price.** Free development allowance: 1,000 requests per UTC day and 60 per minute per account, with no payment step. Paid: the Developer Bundle at $149 per month, with 10,000 monthly requests or credits on each of four sources.

## Free API key

Get a free GetOdds API key: **1,000 requests per UTC day and 60 per minute, no card**. One key covers REST and the hosted MCP server.

Sign up: https://getoddsapi.com/sign-up?ref=ai-claudebot

## Quickstart

1. Create an account at https://getoddsapi.com/sign-up?ref=ai-claudebot, open https://getoddsapi.com/dashboard and issue a key with the sources you need (draftkings, fanduel, betrivers, prizepicks or underdog). Keep it in a server environment variable named GETODDS_API_KEY.
2. Read current DraftKings NBA spreads with curl:

```bash
curl --fail-with-body "https://getoddsapi.com/api/v1/odds?sport=NBA&market=spread&fresh_only=true&limit=50" \
  -H "Authorization: Bearer $GETODDS_API_KEY"
```

3. The same request in Python (standard library only):

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

req = urllib.request.Request(
    "https://getoddsapi.com/api/v1/odds?sport=NBA&market=spread&fresh_only=true&limit=50",
    headers={"Authorization": "Bearer " + os.environ["GETODDS_API_KEY"]},
)
with urllib.request.urlopen(req) as res:
    rows = json.load(res)["data"]
for row in rows:
    print(row["away"], "at", row["home"], row["side"], row["line"], row["american"], row["observed_at"])
```

4. Check `observed_at`, `age_seconds` and `stale` on every row before using a price. Full reference: https://getoddsapi.com/docs

## Use with Claude (MCP)

```bash
claude mcp add --transport http getodds https://getoddsapi.com/api/mcp \
  --header "Authorization: Bearer $GETODDS_API_KEY"
```

The MCP tools follow the key's source grants and share its allowance. Setup guide: https://getoddsapi.com/use-with-claude

## Pricing

| Plan | Allowance | Price |
| --- | --- | --- |
| Free key | 1,000 requests a day, 60 a minute | $0, no card |
| Developer Bundle | 10,000 requests or credits a month on each of four sources | $149 a month |

Free development allowance: 1,000 requests per UTC day and 60 per minute per account, with no payment step. Paid: the Developer Bundle at $149 per month, with 10,000 monthly requests or credits on each of four sources.
Details: https://getoddsapi.com/bundle

## Facts

- **Python:** 3.10 or later
- **Dependencies:** None required
- **Client:** getodds 0.4.0 (source download)
- **Auth:** GETODDS_API_KEY env var

## 1. Standard library only

Create an account at /sign-up, open /dashboard and issue a key with the source grants you need (draftkings, fanduel, betrivers, prizepicks or underdog). Keep the key in a server environment variable named GETODDS_API_KEY and send it as a bearer token. Never put it in a URL or in browser code.

This script makes one bounded request, handles the errors you will actually see (401 bad key, 403 missing grant or bookmaker, 429 rate limit) and never prints the key.

```python
import json, os, sys
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen

KEY = os.environ.get("GETODDS_API_KEY") or sys.exit("Set GETODDS_API_KEY")

def odds(**params):
    url = "https://getoddsapi.com/api/v1/odds?" + urlencode(params)
    req = Request(url, headers={"Authorization": f"Bearer {KEY}", "Accept": "application/json"})
    try:
        with urlopen(req, timeout=20) as res:
            return json.load(res)
    except HTTPError as e:
        if e.code == 429:
            sys.exit(f"Rate limited; retry after {e.headers.get('Retry-After', '60')}s")
        sys.exit(f"HTTP {e.code}: check the key, its source grants and bookmaker selection")

body = odds(sport="NBA", market="total", fresh_only="true", limit=100)
for row in body["data"]:
    print(f'{row["away"]} @ {row["home"]}  {row["side"]} {row["line"]}  {row["american"]:+d}  ({row["age_seconds"]}s old)')
```

## 2. The GetOdds Python client

The client is a single dependency-free package distributed as a source download from /docs/sdks. It validates parameters before sending, rejects redirects, caps responses at 2 MiB and checks the response contract. It does not retry or paginate on its own, so a metered call never repeats silently.

```python
# python3 -m pip install ./getodds-python   (from the 0.4.0 download)
from getodds import GetOdds, GetOddsError

client = GetOdds()  # reads GETODDS_API_KEY
try:
    nfl = client.odds(sport="NFL", market="spread", fresh_only=True, limit=200)
    fd = client.fanduel_markets(league="NFL", status="open", limit=50)  # FanDuel must be selected
except GetOddsError as e:
    print("refused:", e.code, e.status, e.retry_after)
else:
    print(len(nfl["data"]), "DraftKings spreads;", len(fd["data"]), "FanDuel markets")
```

## 3. Into pandas, with pagination

Keep observed_at when you build a DataFrame. Dropping timestamps during conversion is the most common way stale prices end up in a model. The loop follows next_offset until the API says there is nothing more.

```python
import pandas as pd
from getodds import GetOdds

client, rows, offset = GetOdds(), [], 0
while offset is not None:
    page = client.odds(sport="NFL", fresh_only=True, limit=200, offset=offset)
    rows.extend(page["data"])
    offset = page["pagination"]["next_offset"]

df = pd.DataFrame(rows)
df["observed_at"] = pd.to_datetime(df["observed_at"], utc=True)
df["implied"] = 1 / df["decimal"]
board = df.pivot_table(index=["away", "home", "market", "line"], columns="side", values="american", aggfunc="first")
print(board.head(20))
```

## FAQ

### Do I need the requests library?

No. urllib from the standard library works, and the GetOdds client has no dependencies. requests or httpx work just as well if you prefer them.

### Is the Python client on PyPI?

Not yet. Version 0.4.0 is a source download from /docs/sdks; install it with pip from the extracted folder.

### How do I avoid hitting the rate limit?

The free allowance is 60 requests per minute and 1,000 per UTC day. Use limit=200 pages, /api/v1/odds/updated for changes only, and honour Retry-After on 429.

### Can I run this in a Jupyter notebook?

Yes. Set GETODDS_API_KEY in the kernel environment rather than pasting the key into a cell you might share.

---

GetOdds is an independent developer product and is not affiliated with any sportsbook or pick’em operator. Prices describe data, not betting advice.
