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.

ColumnExampleMeaning
game_idnfl-20260913-nyj-tenStable id; same across every version and file
kickoff_utc2026-09-13T17:00:00ZScheduled kickoff
away / homeNew York Jets / Tennessee TitansFull names; away_abbr / home_abbr where known
spread_home-10.5Model spread, home side; negative = home favoured
total44.8Model total points
win_prob_home0.585Moneyline win probability, calibrated. Soccer adds win_prob_draw
fair_ml_home / fair_ml_away-141 / +141No-vig American price implied by the probability
fair_spread_price / fair_total_price-104 / -110Fair price at the model line
market_spread_home / market_total / market_ml_*-9.5 / 45.0Consensus market line at publish time, so you see the gap
h1_spread_home / h1_total / h1_win_prob_home-5.5 / 22.5 / 0.57First-half numbers where modelled
sd_margin / sd_total13.1 / 10.4Model uncertainty in points; use it to size confidence
published_utc2026-09-08T11:00:14ZWhen this row's numbers were published
version / week_key2 / w01Version of the week's board this row belongs to; w01.. weekly, dYYYY-MM-DD daily
sourceboardboard = every-game emitter; predictions = every-game margin/total table; selections = partial board while a league's every-game feed is being connected
feed_refbbd-12-3f9a1cYour 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 keyWhat it is
board.csv / .json / .xlsxyesThe current (or requested) week's board
history.csv / .jsonyesFinal pre-kick row for every past game we hold for the board
free.csv / .xlsx / .jsonnoLast completed week, final version. The try-before-you-pay file
ledger.csvnoClosing-line ledger: every published line, the closing price, and whether it beat the close
metrics.csvnoGraded record per market, closing-line summary, RMSE against the book where computed. Current season only
versions.json / status.jsonnoVersion 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

Boards and seasons

BoardCodeSeasonNow
NFLnflSep to FebLive 16 games this week
College football (FBS)cfbAug to JanPreview 38 games priced, full board switching on
NBAnbaOct to JunOff season
College basketballcbbNov to AprOff season
WNBAwnbaMay to OctPreview full board switching on
NHLnhlOct to JunOff season
MLBmlbMar to OctPreview 14 games priced, full board switching on
AFLaflMar to SepPreview 2 games priced, full board switching on
NRLnrlMar to OctPreview 4 games priced, full board switching on
Premier LeagueeplAug to MayPreview full board switching on
La LigalaligaAug to MayPreview full board switching on
BundesligabundesligaAug to MayPreview full board switching on
Serie AserieaAug to MayPreview full board switching on
Ligue 1ligue1Aug to MayPreview full board switching on
MLSmlsyear-roundPreview full board switching on
EFL ChampionshipeflchampAug to MayPreview full board switching on
BrasileirãobrasileiraoApr to DecPreview full board switching on
A-LeaguealeagueOct to MayOff season
Tennis (ATP + WTA)tennisyear-roundPreview 8 games priced, full board switching on
UFC / MMAufcyear-roundPreview 12 games priced, full board switching on
Cricketcricketyear-roundPreview 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

Bet Better Pty Ltd · ABN 66 670 726 691 · LicenceModel output is information, not advice.