All notable changes to betano-odds-api are recorded here.
- 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.
/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.
- 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.
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.
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.
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.
- 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.