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
See the columns with real numbers on any board page: this week's games, market lines, gap, versions and last week in full.
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 |
Player props file
Every board with player markets also publishes a props file: f=props.csv, props.json or props.xlsx (same key, same address). One row per player and market at the main line, the line most books quote; ties go to the line nearest our projection. Yes/no markets (anytime scorer, anytime TD, anytime try) use line 0.5. Last week's props are free: f=props-free.csv.
| Column | Example | Meaning |
|---|---|---|
| player · team | Patrick Mahomes · KC | Player name as the books list it; team when known |
| market · market_label | player_pass_yds · Passing yards | Market key and its plain-English name |
| line | 274.5 | The main line |
| projection · sd | 281.4 · 42.0 | Our expected value for the stat and our uncertainty around it |
| prob_over · prob_under | 0.564 · 0.436 | Our chance the player goes over / under the line |
| fair_over_price · fair_under_price | -129 · +129 | Those chances as American odds, no margin |
| market_over_price · market_under_price · n_books | -115 · -105 · 6 | Consensus book prices at publish time and how many books quoted the line |
| game_id · kickoff_utc · away · home | — | Same game keys as the board file, so props join to games |
| published_utc · version · week_key · model_version · feed_ref | — | As on the board 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 | Live 7 games this week |
| AFL | afl | Mar to Sep | Live 2 games this week |
| NRL | nrl | Mar to Oct | Live 4 games this week |
| Premier League | epl | Aug to May | Live 6 games this week |
| La Liga | laliga | Aug to May | Live 7 games this week |
| Bundesliga | bundesliga | Aug to May | Live 1 games this week |
| Serie A | seriea | Aug to May | Live 8 games this week |
| Ligue 1 | ligue1 | Aug to May | Live 3 games this week |
| MLS | mls | year-round | Live 28 games this week |
| 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 3 games priced, full board switching on |
| UFC / MMA | ufc | year-round | Live 13 games this week |
| Cricket | cricket | year-round | Live 2 games this week |
Status
GET /data/feed.aspx?f=status.json returns every board's latest week, version, game count and publish time. Right now: 12 boards live, 138 games on this week's boards, last publish Wed 9 Sep, 11:51 pm 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.