# Changelog of Betano Odds Scraper — Live + All Markets, 14 Countries (`blackfalcondata/betano-scraper`) Actor

- **URL**: https://apify.com/blackfalcondata/betano-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/blackfalcondata/betano-scraper.md

## Changelog

### 0.1.58 (2026-09-12)

- A run that names leagues, URLs or searches and finds NONE of them now fails with a clear reason instead of finishing green with no rows. Naming two football leagues while only basketball, tennis and baseball are selected used to end as a successful empty run, charged, with the message "Nothing to fetch for this input" — which blamed the input without saying what about it. The run now stops and says which thing to check. A run that names nothing is unaffected and still finishes normally with whatever the book had.
- When the Betano site cannot be reached, the run no longer fails. Being unable to read the book is not a fault in your request and the run did the right thing with what it got, so it now finishes and tells you the site did not answer, that this is not a result about your search, and to try again later. Runs are no longer marked failed for an outage at the source.
- The Leagues field now says that names are matched against the league list of the sport(s) you selected, and that a football league name finds nothing if you only selected basketball.

### 0.1.32 (2026-08-31)

- A match that has already kicked off now returns the markets the book prices for it, whoever found it. A running match keeps only a remnant on its pre-match page, and until now only matches found through "Live matches" read the in-play surface. Measured on a 120-event run: the 34 rows that were in play carried 33 markets between them against the 736 the book advertised, one of them none at all, and not one carried a score. Those rows now also carry the score, the clock, the period and the suspension flag.
- Monitoring now reports a market the book has SUSPENDED as changed. A suspended market keeps its prices but they cannot be taken, and a price-only comparison called that "unchanged": measured over 240 snapshot pairs on ten running matches, 222 selections flipped between takeable and suspended inside pairs whose prices were byte-identical. Runs whose markets are open are unaffected and are not re-sent.
- Every row carries `fetchedAt`, the moment that event's odds were read. `scrapedAt` is when the RUN started, and a run reading many books takes minutes — measured at 547 seconds for a 29-sport run, whose last row was dated nine minutes earlier than it was read. `isLive` now reads the same clock, so a match that kicks off during a run is no longer reported as not started.
- Comparing a match across country books now compares the SAME market. Some books lead with an enhanced-odds variant of the match result ("SuperOdds", "MegaCote"), and taking each book's first few markets put a promotional price next to a standard one: 3 of 44 comparisons measured had no market in common at all. A book that prices no comparable market now contributes nothing rather than something else.
- An outright no longer invents a home and an away side. 11 of 48 outright rows carried one — the first two riders of a Tour de France field, two constructors out of a Formula 1 grid, and one race whose own title was split in half.
- In-play markets carry `qualifier` and `instance`, which is what tells markets of the same type apart. On one match with 804 markets, 66 of them were indistinguishable on the fields published before: eight "next goal before 10:00 … 80:00", the same eight per team, and eight more per minute band.
- Ticking "All leagues" together with "Live matches" now returns both. The catalogue sweep was being cancelled by the live switch, so the run returned in-play matches only, with no warning.

### 0.1.31 (2026-08-31)

- Live matches now return every market the book prices, not the handful 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 that fetches them had been answering with an empty stub because it stopped reading its query parameters and now reads two headers instead; that is what the run sends.
- A live run no longer reports its coverage as "failed pages". A book answering that it has nothing for one match, which is what a suspended or just-finished match gets, is an answer rather than a page the run failed to reach.

### 0.1.30 (2026-08-31)

- Fractional prices are now the ones a bookmaker's board actually prints. 1.67 is 4/6, not 67/100; 2.5 is 6/4, not 3/2; 1.91 is 10/11. Measured over 14,543 live selections from five country books, 560 of them (3.9%) carried a reduced fraction no board shows, and six prices just over evens came back empty. Decimal, American and implied probability were already right and are unchanged.
- A live match now reports its set score. On tennis, table tennis and volleyball the book's own score is the score of the *game being played*, so a match standing at 2-1 in sets was published as "1 / 2" with no way to recover the match. New `liveSets` (every set played, in order) and `liveSetsWon` (the match score) carry it.
- A market, or a whole event, that the book has pulled is flagged `suspended`. Prices on a suspended market are still shown by the book but cannot be taken, and nothing in the row said so. Pre-match rows report `null` there, because those pages publish no such flag at all, and "not knowable" is not the same answer as "not suspended".
- A live run asking for all markets now keeps the markets the match page carries. They were being fetched and then discarded whenever the in-play feed answered with nothing, which is what that feed does for almost every running match.
- A live run no longer reports its coverage as "failed pages". The in-play feed answering with nothing is that book saying it has nothing on that surface, not a page the run failed to reach, and treating it as a failure marked every live run incomplete for the wrong reason.
- 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; the real codes (anytime, first and last scorer) are unaffected.
- One-row-per-selection output carries `participantId`, `teamId` and `playerId`, so a player or team row can be joined on an id instead of on a name.

### 0.1.22 (2026-08-31)

- Confirmed that the German book simply does not offer five of the sports (esports, table tennis, futsal, boxing, MMA), rather than hiding them behind a different address: it uses the same English page addresses as the other books, and its own competition list names no address for those five. Reaching 18 of 23 sports there is the correct answer for that book.

### 0.1.16 (2026-08-31)

- Market tabs picked alongside "All markets" are no longer ignored. Choosing "All markets" used to discard everything else you selected, so asking for all markets plus player markets fetched only the combined list. That matters because the two are not the same set: on a Premier League fixture the book advertised as having 959 markets, the combined list returned 703 while the individual tabs held 51 markets it did not carry, and 24 that only it carried. Selecting both now returns the union, at one extra lookup per tab. "All markets" on its own is unchanged.

### 0.1.10 (2026-08-30)

- Every sport now works. Only five of the nineteen listed sports could actually be reached: the rest were dropped from the run without an error, because the book only tells us a sport's page address through its "hot coupons" list, and most sports have none. Measured on the Danish book, all fourteen of the excluded sports carry real competitions (esports 23, cricket 15, ice hockey 11, handball 9, rugby union 9, volleyball 7, golf 6, futsal 6, and more). They are now reached by their own page, which is checked before it is used, so nothing is guessed.
- Four sports were missing from the list altogether and are now selectable: Formula 1, motorsport, cycling and speedway. Each carries real competitions on the Danish book.
- A sport the book genuinely does not offer is still reported as such, rather than being treated as a failure.

### 0.1.8 (2026-08-30)

- New `allLeagues` option covers every league the book lists for the chosen sports, instead of only the matches starting in the next few hours. The Danish book alone lists 209 football leagues across 89 regions, 43 basketball and 30 tennis, so this is a much larger run: set `maxEvents` to a number you want to pay for. It is ignored when you name leagues, paste URLs or ask for live matches, since those are already an explicit scope.
- A run covering several sports now returns all of them. The result limit is shared across the run, and the leagues used to be fetched sport by sport, so the first sport could use up the whole limit: a run over 12 sports with a limit of 24 returned 24 football matches and nothing else. Leagues are now spread evenly across the sports you asked for.
- A catalogue run reports complete coverage rather than a time window, because it is not one.
- Monitoring runs keep their state. Turning the option on changes what the run covers, so it starts its own history; runs that do not use it are unaffected and carry on from where they were.

### 0.1.6 (2026-08-29)

- New `includeStats` option attaches Sportradar match statistics to every event that has them: team statistics, per player numbers, lineups and formations, the timeline, head to head history, recent form, league tables, match situations and fun facts. Pick the layers you want under `statsLayers`. Betano publishes the Sportradar match id on the event itself, so the statistics are matched on that id and never guessed from team names or kick-off times.
- Statistics are charged only when a layer you asked for actually arrives. An event with no Sportradar coverage is not billed, and neither is a match that is still days from kick-off: Sportradar publishes lineups, the timeline, player numbers and situations only from around kick-off, so such a match returns the header (teams, venue, referee, competition) and no layers, free of charge. Head to head, form and league tables are available before the match. The lookup also runs only for events that are actually delivered, so a monitoring run that skips unchanged matches, or a run that has reached its result limit, does not pay to enrich rows it will not return.
- Statistics prices match the standalone Sportradar actor exactly, so combining odds and statistics in one run costs the same as running the two separately. What you get instead is one run and one dataset with both joined.
- In incremental mode a match is re-emitted when its odds change. A match whose lineup or timeline moved while its prices did not is not returned again, which keeps a live match from being re-sent and re-charged every minute.
- Statistics ride the one-row-per-event output. The one-row-per-selection table is unchanged.

### 0.1.5 (2026-08-28)

- New `searchQueries` input finds matches by name. Type a team, player or competition ("Bayern", "Man City") and every matching upcoming or live match comes back with its markets, with no URL or id to look up first. Betano does the matching, so short and full names both work, and each match is returned once even when the search hits several of its markets.
- New `playerUrls` input takes Betano player page URLs. Every upcoming match of that player comes back carrying that player's own markets: shots on target, total shots, assists, tackles, fouls for and against, offsides, plus the goalscorer and card prices filtered to just that player. On the page measured, six of the match's markets were the player's own, and they arrive with the listing row, so no separate match page is opened for them. Player URLs also work in `startUrls`, where they used to be skipped.
- A region URL now returns the whole region. Pasting a region page such as the Danish football region used to return only its first league, because that is all the page itself carries; the run now covers every league of the region. Measured on Denmark: 6 matches before, 30 after. Leagues you also listed by name are not fetched twice, and a league that is out of season is skipped with a note instead of failing the run.
- The `daysAhead` window applies to player pages, the same way it already applied to league, team and region pages.
- Monitoring runs keep their state. Adding the new input does not change the identity of a run that does not use it, so an existing scheduled run carries on from where it was rather than reporting everything as new.

### 0.1.4 (2026-08-28)

- Market pages that did not load are no longer billed. The extra charge for opening a match's full market set is now made per match, only for the ones whose page actually came back, instead of once for every match in a run that asked for market tabs.
- Events you list in `eventUrls` keep their real kick-off time, sport, region and match URL. They used to be built from a placeholder that the match page only partly overwrote, so a pasted event could come back dated 1970, flagged as in play and filed under the run's first sport. An event whose page cannot be read is now left out rather than returned with those placeholders in it.
- Fewer requests per match in all-markets mode. Getting every market used to fetch the match page twice at full size and throw the first copy away; it now takes one small page plus the full one, and keeps both.
- The `daysAhead` window now applies to a Betano sport page pasted into `startUrls`, the same way it already applied to league, team and region pages.
- A run where a whole sport could not be read is reported as partial coverage. In monitoring runs that stops the missing sport's matches from being reported as gone when they are only unread.
- An event you list in `eventUrls` that also turns up in a league or coupon listing now gets its full match page, instead of quietly falling back to the headline prices from the listing.

### 0.1.3 (2026-08-28)

- New `sports` input takes several sports in one run. The coupon, the league names you list, the competition winners and the live matches are each done for every sport you picked, and every row still says which sport it is. The single `sport` select keeps working exactly as before and is what a run falls back to when `sports` is empty. Competition winners are now fetched per sport as well, so a tennis or basketball run returns that sport's outrights instead of the book's default sport.
- New `includeLogos` input adds branding to every event: `homeTeamLogo`, `awayTeamLogo`, `homeJerseyColor`, `awayJerseyColor`, `leagueLogo` and `regionFlag`. Betano's coverage is partial, so it fills kit colours for most clubs, crests mainly for national teams, and logos and flags for the leagues and regions the book promotes; anything it does not publish comes back as null. It is off by default and costs one extra lookup per country in the run.

### 0.1.2 (2026-08-28)

- New `liveMatches` input returns every match of your sport that is in play right now, next to the pre-match rows. Each live row carries the running score, the match clock, the period and the live prices, and a match that also appears in a league or coupon listing is still returned only once.
- With `markets` set to anything other than `["main"]`, every live match is opened for its full in-play market set instead of the two or three the live list shows (113 markets on one match, measured). Cross book comparison is skipped for live rows, because a second book's in-play price is already stale by the time it arrives.

### 0.1.1 (2026-08-28)

- Team pages are now a scope of their own. Paste a Betano team page URL into the new `teamUrls` input (or into `startUrls`) and you get every upcoming match of that team, with the league filled in, priced by whichever country site the URL came from.
- Competition winners now follow the leagues you picked. With `includeOutrights` on, a run that named leagues returns the outright markets for those leagues instead of every competition the sport has, so a Premier League run no longer comes back with 74 other winner markets.
- New `daysAhead` input bounds how far into the fixture list a run reaches: only matches starting within that many days from now. It applies to leagues, teams, regions and pasted start URLs, and `0` (the default) keeps every upcoming match Betano lists.
- Bulgaria (betano.bg) joins the country books, bringing the total to 14. It is available both as the book that prices a run and as a `compareCountries` entry.

### 0.1.0 (2026-08-28)

First release.

- Pre-match odds from 13 Betano country books (Germany, Denmark, Portugal, Romania, Brazil, Czechia, Peru, Argentina, Chile, Ecuador, Nigeria, Ghana and Ontario) across 19 sports, with team and league names in English on the books that publish them and the local language as the fallback.
- Choose how deep to go per run: `main` returns the headline markets shown on the fixture list, `all` returns every market Betano prices on the match (692 on a Premier League game), and named groups (players, goals, corners, cards, handicap, asian, halves, specials, minutes, stats) return just the ones you asked for.
- Compare one match across books: `compareCountries` prices the same event in several Betano countries and returns them together on the row as `oddsByCountry`, so you can see where a price differs.
- Pick a scope that suits the job: a coupon window of the next 3, 12 or 24 hours, specific leagues, or explicit event URLs and ids.
- Name leagues however you have them: `leagues` accepts league ids, plain names ("Premier League", "laliga", "England Premier League") and Betano URLs, and resolves them against the book's own league list.
- Two output shapes: `event` mode gives one row per match with markets nested, and `selection` mode flattens to one row per selection, which drops straight into a spreadsheet or a database table.
- Odds in the format you work in: decimal, fractional or American, plus implied probability on every selection. Set `oddsFormat: "all"` to get them side by side.
- Monitor price moves without paying for stale rows: `incrementalMode` remembers the odds it last saw and returns only the events whose prices actually changed, tagged as new or updated.
