$3 day pass or $0.0015 per call. Betano odds as a REST API: pre-match and live with score and clock, 600+ markets per match · the same match priced across 13 country books · search by team name · one HTTP call and JSON back with no batch run.
POST /day-pass now activates the pass only after its charge is recorded. If the charge cannot be recorded the request answers 500 and no pass is granted, so it is safe to retry.
0.1.5 - 2026-10-07
Brazil is no longer offered as the default country book, and the documentation lists 13 books, while Betano's Brazilian site stays closed by government order. A request with country=BR still answers 503BOOK_OFFLINE, uncharged. Brazil comes back in a later release once the site reopens.
All notable changes to betano-odds-api are recorded here.
0.1.4 - 2026-10-07
country=BR now answers 503 with error: "BOOK_OFFLINE" at once, and is never charged. Since 6 October 2026 Betano's Brazilian site is offline by government order, like every licensed Brazilian betting site, so there are no odds to read; until now the same request answered SOURCE_UNAVAILABLE after its deadline. BR inside compare is skipped. Every other country is unaffected, and Brazil is switched back on in a later release once the site reopens.
A maximum cost per run is now honoured. Once it is spent, priced lookups answer 402 with error: "BUDGET_REACHED" instead of being served unbilled; an active day pass still answers. Runs without a limit are unaffected.
Odds and lookups - 2026-09-29
Chile works again. Betano moved its Chilean site to a new address, and every Chile lookup since then answered as if the source were down. Links copied from the old Chilean address are still recognised as Chile.
/coupon returns matches again. Betano moved the "next hours" events behind a separate calendar on 2026-09-25, and since then the coupon answered an empty list on every book.
League names the book itself does not use now find the league: common sponsor names ("Chance Liga", "Liga Portugal Betclic", "PKO Ekstraklasa"), a country adjective in front of a league ("Scottish Premiership"), and women's competitions written in words ("champions league femenina" finds "Champions League (W)").
New isMainLine on every selection: the book's own main line of a multi-line market, for example Over/Under 2.5, on both sides of it. Player markets now also carry jerseyNumber and lastGamesStats, the player's value for that statistic in recent matches as the book shows them.
Pricing - 2026-09-29
api-call $0.002 -> $0.0015 per successful lookup ($1.50 per 1,000). The billing unit is unchanged: one lookup, whatever it returns.
Platform usage (compute and data transfer) is now included in the price; it is no longer billed on top.
day-pass stays $3.00 for 24 hours, and the start fee stays $0.0005. No code change.
0.1.3 - 2026-08-31
A match that has already kicked off returns the markets the book prices for it on every route, not just /live. A running match keeps only a remnant on its pre-match page; measured on the batch sibling, in-play rows enriched from that page carried 33 markets between 34 of them against the 736 the book advertised, one of them none at all, and none of them a score. Those rows now carry the score, the clock, the period and the suspension flag too.
Every record carries fetchedAt, when THAT event's odds were read. scrapedAt is when the request started, and a markets=all request over many events spans tens of seconds.
compareCountries compares the SAME market across books. Some books lead with an enhanced-odds variant of the match result ("SuperOdds", "MegaCote"); taking each book's first few markets put a promotional price beside a standard one, and 3 of 44 comparisons measured shared no market at all. A book with nothing comparable now returns nothing for that book.
An outright no longer reports a home and an away side. 11 of 48 outright rows carried one, naming the first two entrants of a field — or, on one race, the two halves of its own title.
In-play markets carry qualifier and instance, the fields that tell markets of one type apart: 66 of 804 markets on a single match were otherwise indistinguishable.
0.1.2 - 2026-08-31
/live?markets=all returns every market the book prices for a running match, not the two or three its in-play list summarises. Measured on the twelve deepest matches running at the time: 47 markets before, 561 after, against the 569 the book advertises. The lookup behind it had been answering with an empty stub because it stopped reading its query parameters and now reads two headers instead.
0.1.1 — 2026-08-31
Fractional prices are the ones a board prints. 1.67 is 4/6, not 67/100; 2.5 is 6/4, not 3/2. Measured over 14,543 live selections from five country books, 560 of them (3.9%) carried a reduced fraction no bookmaker shows, and six prices just over evens came back empty.
A live match now reports its SET score. liveScore on tennis, table tennis and volleyball is the score of the game being played, so a match standing at 2-1 in sets read "1 / 2". The new liveSets and liveSetsWon carry the match itself.
A market, or a whole event, that the book has pulled is flagged suspended, so a price that cannot be taken is no longer indistinguishable from one that can. Pre-match rows report null there, because those pages publish no such flag at all.
/live?markets=all reads the match page as well as the in-play feed. It used to fetch that page and then discard it whenever the feed answered with nothing, which is what the feed does for almost every running match.
A finished match still sitting in the in-play list is no longer reported as running.
A table column that carries only a rendering hint no longer arrives as a market type: 216 selections reported columnType: "market", which is not a Betano market code.
Flat selection rows carry participantId, teamId and playerId, so a player row can be joined on an id rather than on a name.
0.1.0 — 2026-08-28
First version. The standby (always-on HTTP) sibling of betano-scraper: the same odds, asked
for one request at a time instead of one batch run at a time.
Endpoints
GET /health and GET /sports — free. Version, and the 19 sports with their keys and ids.
GET /leagues — the league catalogue of one country book and sport, with the ids /league
takes.
GET /coupon — matches starting in the next 3, 12 or 24 hours. When the book does not
publish the window asked for, the next larger one it does publish is served.
GET /league/{leagueId} and GET /league?name= — one league's remaining fixture list, by id
or by name resolved against the book's own catalogue.
GET /event/{eventId} — one match. markets=all returns every market the book prices
(measured: 637 markets and 3,882 selections on a Premier League match).
GET /live — every match in play right now, with score, clock and period.
GET /search?term= — Betano's own site search, resolved to full event records.
GET /team?url= and GET /player?url= — a team's or player's page, taken as the address the
caller pasted. A player's page carries their prop markets inline.
GET /day-pass and POST /day-pass — status and purchase.
Every priced endpoint takes country, markets, odds, compare and logos; the fixture-list
endpoints also take max, daysAhead and includeLive.
Pricing
api-call $0.002, charged once per successful request to a priced endpoint whatever it
returns. A request that fails validation, 404s or times out is not charged.
day-pass $3.00, charged once by POST /day-pass; for 24 hours no api-call is charged,
across every run of that account.
apify-actor-start $0.0005. Platform usage is billed by Apify at the caller's plan rates.
Notes
The parsing, transport and normalisation layers are vendored byte-identical from
betano-scraper, so both actors emit the same EventRecord.
changeType is present on every row and always null: change classification needs state
between runs, which a single request does not have. Compare priceHash instead.