One address, three endings
Everything is a URL you can open in a browser. Add your key. Change f= to change the format, b= to change the board. Add week= and version= to fetch exactly what you used on Tuesday.
# This hour's NFL board as a spreadsheet file GET https://betbetter.world/data/feed.aspx?b=nfl&f=board.csv&key=bbd_live_… # Same thing for an app GET https://betbetter.world/data/feed.aspx?b=nfl&f=board.json&key=bbd_live_… # Excel workbook (board + dictionary tab) GET https://betbetter.world/data/feed.aspx?b=nfl&f=board.xlsx&key=bbd_live_… # A specific week and version GET https://betbetter.world/data/feed.aspx?b=nfl&f=board.csv&week=w01&version=2&key=… # Everything graded so far (final pre-kick row per game) GET https://betbetter.world/data/feed.aspx?b=nfl&f=history.csv&key=… 200 OK · X-BB-Week: w01 · X-BB-Version: 2 · X-BB-Published: 2026-09-08T11:00:00Z · X-BB-Source: board
The key can also travel as a header: Authorization: Bearer bbd_live_… is accepted wherever key= is. Opened from your feed page while signed in, no key is needed at all.
Google Sheets and Excel
Google Sheets
In any cell: =IMPORTDATA("https://betbetter.world/data/feed.aspx?b=nfl&f=board.csv&key=bbd_live_…"). Sheets refreshes it about hourly. Put it on its own tab and build your work on other tabs that reference it.
Excel (Windows and Mac)
Data → From Web → paste the same address → Load. Right-click the table → Refresh, or set Query Properties → refresh every 60 minutes. Excel 365 also accepts =WEBSERVICE(...) for a single value.
Numbers, LibreOffice, anything else
Download the CSV or XLSX and open it. Same columns, same numbers.
Board columns
Every row is one game. Column names never change between boards; a column that doesn't apply to a sport is simply empty.
| Column | Example | Meaning |
|---|---|---|
| game_id | nfl-20260913-nyj-ten | Stable id; same across every version and file |
| kickoff_utc | 2026-09-13T17:00:00Z | Scheduled kickoff |
| away / home | New York Jets / Tennessee Titans | Full names; away_abbr / home_abbr where known |
| spread_home | -10.5 | Model spread, home side; negative = home favoured |
| total | 44.8 | Model total points |
| win_prob_home | 0.585 | Moneyline win probability, calibrated. Soccer adds win_prob_draw |
| fair_ml_home / fair_ml_away | -141 / +141 | No-vig American price implied by the probability |
| fair_spread_price / fair_total_price | -104 / -110 | Fair price at the model line |
| market_spread_home / market_total / market_ml_* | -9.5 / 45.0 | Consensus market line at publish time, so you see the gap |
| h1_spread_home / h1_total / h1_win_prob_home | -5.5 / 22.5 / 0.57 | First-half numbers where modelled |
| sd_margin / sd_total | 13.1 / 10.4 | Model uncertainty in points; use it to size confidence |
| published_utc | 2026-09-08T11:00:14Z | When this row's numbers were published |
| version / week_key | 2 / w01 | Version of the week's board this row belongs to; w01.. weekly, dYYYY-MM-DD daily |
| source | board | board = every-game emitter; predictions = every-game margin/total table; selections = partial board while a league's every-game feed is being connected |
| feed_ref | bbd-12-3f9a1c | Your licence reference, on every row of every file |
Versions and timestamps
A week's board is published as version 1 (the opening board), then re-published as a new version whenever the model re-runs and numbers move. Every version is kept. versions.json lists them; version= fetches one. Each row's published_utc is the audit trail: the number, and when it existed.
GET /data/feed.aspx?b=cfb&f=versions.json
{"board":"cfb","week_key":"w03","current_version":3,"versions":[{"version":1,"published_utc":"2026-09-06T02:14:00Z","games":87,"note":"Opening board"}, …]}
Kickoff freezes a game: its last pre-kick row is the one that gets graded and the one that appears in history.csv.
Other files
| f= | Needs key | What it is |
|---|---|---|
| board.csv / .json / .xlsx | yes | The current (or requested) week's board |
| history.csv / .json | yes | Final pre-kick row for every past game we hold for the board |
| free.csv / .xlsx / .json | no | Last completed week, final version. The try-before-you-pay file |
| ledger.csv | no | Closing-line ledger: every published line, the closing price, and whether it beat the close |
| metrics.csv | no | Graded record per market, closing-line summary, RMSE against the book where computed. Current season only |
| versions.json / status.json | no | Version list for a week; live status for every board |
JSON shape
{
"board": "nfl", "board_name": "NFL", "week_key": "w01", "version": 2,
"published_utc": "2026-09-08T11:00:14Z", "source": "board", "games": 16, "feed_ref": "bbd-12-3f9a1c",
"rows": [ { "game_id": "nfl-20260913-nyj-ten", "kickoff_utc": "2026-09-13T17:00:00Z", "away": "New York Jets", "home": "Tennessee Titans",
"spread_home": -10.5, "total": 44.8, "win_prob_home": 0.585, "fair_ml_home": -141, "fair_ml_away": 141, … } ]
}
Numbers are numbers (not strings). Missing values are null. Response headers repeat week, version, publish time and source, and an ETag lets you poll cheaply with If-None-Match (304 when nothing changed).
Webhooks
Save an https address on your feed page and we POST to it every time a board version is published. Body:
{"event":"board.version","board":"nfl","week_key":"w01","version":2,"published_utc":"2026-09-08T11:00:14Z","games":16,"note":"Injury re-run","url":"https://betbetter.world/data/feed.aspx?b=nfl&f=board.json&week=w01&version=2"}
Headers: X-BB-Timestamp (unix seconds) and X-BB-Signature = HMAC-SHA256(secret, timestamp + "." + body) in hex. The secret is shown on your feed page. Respond 2xx within 10 seconds; we retry on the next run.
Rate limits and fair use
- 60 requests a minute per key (240 on the all-boards plan). 429 with
Retry-Afterbeyond that. Boards change at most a few times a day; hourly polling is plenty. - One key per licence, for one company. Distinct-IP patterns that look like sharing are flagged; we email before anything else happens.
- Uptime target 99.5%. The status file below is the truth; the free files stay up even when keyed files are paused.
Boards and seasons
| Board | Code | Season | Now |
|---|---|---|---|
| NFL | nfl | Sep to Feb | Live 16 games this week |
| College football (FBS) | cfb | Aug to Jan | Preview 38 games priced, full board switching on |
| NBA | nba | Oct to Jun | Off season |
| College basketball | cbb | Nov to Apr | Off season |
| WNBA | wnba | May to Oct | Preview full board switching on |
| NHL | nhl | Oct to Jun | Off season |
| MLB | mlb | Mar to Oct | Preview 14 games priced, full board switching on |
| AFL | afl | Mar to Sep | Preview 2 games priced, full board switching on |
| NRL | nrl | Mar to Oct | Preview 4 games priced, full board switching on |
| Premier League | epl | Aug to May | Preview full board switching on |
| La Liga | laliga | Aug to May | Preview full board switching on |
| Bundesliga | bundesliga | Aug to May | Preview full board switching on |
| Serie A | seriea | Aug to May | Preview full board switching on |
| Ligue 1 | ligue1 | Aug to May | Preview full board switching on |
| MLS | mls | year-round | Preview full board switching on |
| EFL Championship | eflchamp | Aug to May | Preview full board switching on |
| Brasileirão | brasileirao | Apr to Dec | Preview full board switching on |
| A-League | aleague | Oct to May | Off season |
| Tennis (ATP + WTA) | tennis | year-round | Preview 8 games priced, full board switching on |
| UFC / MMA | ufc | year-round | Preview 12 games priced, full board switching on |
| Cricket | cricket | year-round | Preview full board switching on |
Status
GET /data/feed.aspx?f=status.json returns every board's latest week, version, game count and publish time. Right now: 1 boards live, 94 games on this week's boards, last publish Tue 8 Sep, 8:58 am ET. Something wrong? hello@betbetter.world, one business day.
Changelog
- 2026-09-08 · Bet Better Data launched: NFL and college football boards, four delivery doors, versions, ledger and metrics files, webhooks, pilot and licence plans. Licence PL-2026-09.