# Sports odds in Google Sheets and Excel

Paste the Apps Script below into Extensions > Apps Script, save your GetOdds key as a script property, and run it: the sheet fills with NFL odds, one row per side, with lines, American and decimal prices and capture times. A time trigger refreshes it every 15 minutes, which is 96 requests a day. Excel users can do the same with Power Query.

Canonical page: https://getoddsapi.com/odds-api-google-sheets

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

- **Google Sheets:** Apps Script, no add-on
- **Excel:** Power Query (Get Data)
- **Refresh:** Every 15 minutes = 96 requests/day
- **Key storage:** Script properties

## Google Sheets: the script

Create an account at /sign-up and issue a key with the draftkings grant in /dashboard. In your sheet open Extensions > Apps Script, paste this code, then add a script property named GETODDS_API_KEY under Project Settings. Keeping the key in script properties means people you share the sheet with do not see it in a cell.

```javascript
const SPORT = 'NFL'; // NFL, NBA, NHL, NCAAF, NCAAB, EPL or MLS

function refreshOdds() {
  const key = PropertiesService.getScriptProperties().getProperty('GETODDS_API_KEY');
  if (!key) throw new Error('Add GETODDS_API_KEY in Project Settings > Script properties');
  const url = 'https://getoddsapi.com/api/v1/odds?sport=' + SPORT + '&fresh_only=true&limit=200';
  const res = UrlFetchApp.fetch(url, {
    headers: { Authorization: 'Bearer ' + key }, muteHttpExceptions: true, followRedirects: false,
  });
  if (res.getResponseCode() !== 200) throw new Error('GetOdds HTTP ' + res.getResponseCode());
  const body = JSON.parse(res.getContentText());

  const cols = ['starts_at', 'away', 'home', 'market', 'side', 'line', 'american', 'decimal', 'observed_at', 'age_seconds'];
  const rows = body.data.map(r => cols.map(c => (r[c] === null || r[c] === undefined ? '' : r[c])));
  const book = SpreadsheetApp.getActive();
  const sheet = book.getSheetByName(SPORT + ' odds') || book.insertSheet(SPORT + ' odds');
  sheet.clearContents();
  sheet.getRange(1, 1, 1, cols.length).setValues([cols]);
  if (rows.length) sheet.getRange(2, 1, rows.length, cols.length).setValues(rows);
  sheet.getRange(1, cols.length + 2).setValue('Checked ' + body.meta.checked_at + (body.pagination.has_more ? ' (more rows available)' : ''));
}

function installTrigger() {
  ScriptApp.newTrigger('refreshOdds').timeBased().everyMinutes(15).create();
}
```

## Spreadsheet formulas that work with these columns

With the layout above (American odds in column G, decimal in column H), these formulas give implied probability and the margin on a two-way market without converting anything by hand.

| Goal | Formula |
| --- | --- |
| Implied probability of a row | =1/H2 |
| Overround of a two-row market | =1/H2+1/H3-1 |
| No-vig probability of row 2 | =(1/H2)/(1/H2+1/H3) |
| Profit on a $100 stake at American odds | =IF(G2>0,G2,10000/ABS(G2)) |

## Excel: Power Query

In Excel, put your key in a one-cell table named ApiKey, then create a blank query (Data > Get Data > From Other Sources > Blank Query), open the Advanced Editor and paste this. Choose Anonymous when Excel asks how to connect; the key travels in the header. Refresh with Data > Refresh All, or set the query to refresh every 15 minutes in its properties. The key is stored in the workbook, so do not share that file.

```text
let
    Key = Excel.CurrentWorkbook(){[Name="ApiKey"]}[Content]{0}[Column1],
    Response = Json.Document(Web.Contents("https://getoddsapi.com/api/v1/odds", [
        Query = [sport = "NBA", fresh_only = "true", limit = "200"],
        Headers = [Authorization = "Bearer " & Key, Accept = "application/json"]
    ])),
    Odds = Table.FromRecords(Response[data])
in
    Odds
```

## FAQ

### Is there a GetOdds add-on for Google Sheets?

No add-on is needed. The Apps Script on this page is the whole integration. If you want a packaged add-on with a menu, The Odds API publishes Sheets and Excel add-ons.

### How often can my sheet refresh for free?

Every 15 minutes uses 96 requests a day, and every 5 minutes uses 288, both well inside 1,000 requests per UTC day. Apps Script time triggers cannot run more often than every minute.

### Why is my sheet empty?

fresh_only=true returns only prices captured in the last 15 minutes. Off-season, or between slates, that can be zero rows. The checked_at cell shows the request ran.

### Can I pull FanDuel prices into Sheets?

Yes, after selecting FanDuel on the account. /api/v1/sportsbook/markets returns nested selections, so flatten each market's selections array into rows in the script.

---

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