# Arbitrage betting API with stakes and stated assumptions

GET /api/v1/arbitrage-bets compares recent DraftKings and FanDuel prices for the NBA, NFL, NCAAB and NCAAF and returns every two-way combination where backing both sides returns more than the stake, with stakes and payouts rounded to whole cents. Each result states the settlement assumptions it relies on and is labeled guaranteed_profit=false, because prices move and limits apply.

Canonical page: https://getoddsapi.com/arbitrage-api

**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

- **Books compared:** DraftKings vs FanDuel
- **Leagues:** NBA, NFL, NCAAB, NCAAF
- **Markets:** Moneyline (basketball), spread, total
- **Price age:** 30 to 600 seconds, your choice

## Request arbitrage results

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.

The key needs both the draftkings and fanduel grants, and both books must be selected on the account. bankroll (default 100, in USD) sizes the hypothetical stakes. max_age_seconds and max_skew_seconds bound how old each quote may be and how far apart the two captures can be.

```bash
# DraftKings is selected by default. FanDuel and BetRivers must be selected once per account.
curl --fail-with-body -X PUT \
  "https://getoddsapi.com/api/v1/bookmakers/selected/select?bookmakers=draftkings,fanduel,betrivers" \
  -H "Authorization: Bearer $GETODDS_API_KEY"

curl --fail-with-body "https://getoddsapi.com/api/v1/arbitrage-bets?league=NBA&event_match=bounded_time&max_age_seconds=300&max_skew_seconds=60&bankroll=500" \
  -H "Authorization: Bearer $GETODDS_API_KEY"
```

## Read a result

Each row has the matched DraftKings and FanDuel events, the market (family, line, sides), both quotes with their ages, and an allocation. The script prints the stakes and the guaranteed-if-both-settle-normally minimum profit.

```python
import json, os
from urllib.request import Request, urlopen

url = "https://getoddsapi.com/api/v1/arbitrage-bets?league=NBA&event_match=bounded_time&bankroll=500"
req = Request(url, headers={"Authorization": "Bearer " + os.environ["GETODDS_API_KEY"]})
with urlopen(req, timeout=20) as r:
    body = json.load(r)

print("coverage:", body["meta"]["coverage"])
for arb in body["data"]:
    dk = arb["event"]["draftkings"]
    print(f'{dk["away"]} @ {dk["home"]}  {arb["market"]["family"]} {arb["market"]["line"]}  margin {arb["allocation"]["margin_percent"]:.2f}%')
    for leg in arb["legs"]:
        print(f'   {leg["bookmaker"]:<10} {leg["side"]:<6} {leg["american"]}  stake ${leg["stake_cents"] / 100:.2f}')
```

## The math, and why it is conditional

For two outcomes with decimal prices d1 and d2, an arbitrage exists when 1/d1 + 1/d2 is below 1. Stakes are split in proportion to 1/d so both outcomes return the same amount. The API does this with exact rational conversion from the American prices, then rounds stakes to cents and payouts down, and only returns the result if it is still positive after rounding.

It is conditional because both bets must be accepted at those prices and settle on the same completed game, including overtime. That rules out football moneylines (ties), whole-number lines (pushes), alternates, halves and quarters. Fees, taxes, stake limits and account restrictions are not modeled; maximum_stake is always null.

## Expect empty results, and that is honest

Two major regulated books rarely disagree enough to create an arb, and when they do it closes fast. In production checks the feed matched events and eligible contract pairs and found zero positive results. The response always reports coverage counts (matched events, eligible pairs, skew rejections) so you can tell "no arb right now" from "no data". If your strategy depends on many books and fast detection, a broader aggregator will find more.

## FAQ

### Is an arbitrage result guaranteed profit?

No. It assumes both prices are accepted together and both bets settle normally on the same game. Every response says guaranteed_profit=false.

### Which sportsbooks does the arbitrage API compare?

DraftKings and FanDuel. BetRivers prices are available separately but are not added to the arbitrage calculation.

### Why does exact matching return fewer games than bounded_time?

Some sources schedule the same game a minute apart. bounded_time allows up to 120 seconds of start-time difference and reports it; exact requires identical starts.

### Does it include player prop arbitrage?

No. Player props, alternate lines and partial-game markets are excluded.

---

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