Skip to contents

Overview

This vignette will outline the ways in which the functions in bettRtab can be used to extract sports and racing betting data from the TAB.


Installation

You can install the released version of bettRtab from GitHub with:

# install.packages("remotes")
remotes::install_github("JaseZiv/bettRtab")

Usage

The functions in this package should work without hitch locally, but running these in an automated way (GitHub Actions, cloud, etc) may lead to requests being blocked.

To get around this, you will need access to proxies and would need to set configs at the start of your scripts similar to the following:

httr::set_config(httr::use_proxy(url = Sys.getenv("PROXY_URL"),
                                 port = as.numeric(Sys.getenv("PROXY_PORT")),
                                 username =Sys.getenv("PROXY_USERNAME"),
                                 password= Sys.getenv("PROXY_PASSWORD")))

Sports Data

To get betting data for any currently available competition market, use the get_sports_market() function.

To get a valid list of values you can pass to the competition_name parameter, look at the competitions.name column in the file here.

Alternatively, the competition_name needed as an argument to this function can be found under the competitions page selector on the TAB website.

epl_futures <- get_sports_market(competition_name = "English Premier League Futures")

glimpse(epl_futures)
#> Rows: 559
#> Columns: 33
#> $ marketId            <chr> "98519821", "98519821", "98519821", "98519821", "9…
#> $ marketName          <chr> "24/25 EPL Winner", "24/25 EPL Winner", "24/25 EPL…
#> $ shortName           <chr> "Eng 24/25 EPL Winner", "Eng 24/25 EPL Winner", "E…
#> $ betOption           <chr> "Winner", "Winner", "Winner", "Winner", "Winner", …
#> $ betOptionSpectrumId <chr> "52", "52", "52", "52", "52", "52", "52", "52", "5…
#> $ betOptionPriority   <chr> "330", "330", "330", "330", "330", "330", "330", "…
#> $ marketUniqueId      <chr> "98519821", "98519821", "98519821", "98519821", "9…
#> $ closeTime           <chr> "2024-08-16T19:00:00.000Z", "2024-08-16T19:00:00.0…
#> $ marketBettingStatus <chr> "Open", "Open", "Open", "Open", "Open", "Open", "O…
#> $ isFuture            <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ onlineBetting       <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ phoneBettingOnly    <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ inPlay              <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ goingInPlay         <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowWin            <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ marketAllowPlace    <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowEachWay        <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowMulti          <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ allowMultiWin       <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ allowMultiPlace     <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowMultiEachWay   <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ numberOfPlaces      <chr> "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", …
#> $ cashOutEligibility  <chr> "Enabled", "Enabled", "Enabled", "Enabled", "Enabl…
#> $ allowBundle         <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ id                  <chr> "494060", "494061", "494062", "494064", "494065", …
#> $ name                <chr> "Man City", "Arsenal", "Liverpool", "Chelsea", "To…
#> $ returnWin           <chr> "2.1", "2.85", "9", "21", "21", "23", "26", "51", …
#> $ returnPlace         <chr> "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", …
#> $ bettingStatus       <chr> "Open", "Open", "Open", "Open", "Open", "Open", "O…
#> $ allowPlace          <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ number              <chr> "494060", "494061", "494062", "494064", "494065", …
#> $ isOpen              <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ sortOrder           <chr> "1", "2", "3", "5", "6", "4", "8", "7", "9", "11",…

We can then inspect to see which betting markets are available:

epl_futures %>% 
  select(betOption) %>% distinct()
#>                       betOption
#> 1                        Winner
#> 2               Top Goal Scorer
#> 3                        Exacta
#> 4  Winner/Top Goalscorer Double
#> 5                         Top 2
#> 6                         Top 4
#> 7                         Top 6
#> 8                    Relegation
#> 9            To Finish Top Half
#> 10        To Finish Bottom Half
#> 11          Team to Finish Last
#> 12                Favourite Out
#> 13         Winner without Big 6
#> 14                 Most Assists
#> 15          Top North West Club
#> 16              Top London Team
#> 17            Top Midlands Team
#> 18            Top Promoted Team

and we can then explore some key markets… say we want to see the odds of relegation in the EPL:

epl_futures %>% 
  filter(betOption == "Relegation") %>% 
  select(marketName, id, name, returnWin)
#>                  marketName     id              name returnWin
#> 1  Eng 24/25 EPL Relegation 200081         Leicester       1.4
#> 2  Eng 24/25 EPL Relegation 200084           Ipswich      1.72
#> 3  Eng 24/25 EPL Relegation 200275       Southampton      2.35
#> 4  Eng 24/25 EPL Relegation 200278           Everton       2.6
#> 5  Eng 24/25 EPL Relegation 200279 Nottingham Forest         3
#> 6  Eng 24/25 EPL Relegation 200283     Wolverhampton       4.5
#> 7  Eng 24/25 EPL Relegation 200293         Brentford         6
#> 8  Eng 24/25 EPL Relegation 200298            Fulham       6.5
#> 9  Eng 24/25 EPL Relegation 200313       Bournemouth        10
#> 10 Eng 24/25 EPL Relegation 200316    Crystal Palace        10
#> 11 Eng 24/25 EPL Relegation 200325          Man City        17
#> 12 Eng 24/25 EPL Relegation 200332          West Ham        21
#> 13 Eng 24/25 EPL Relegation 200354          Brighton        21
#> 14 Eng 24/25 EPL Relegation 200360           Chelsea        21
#> 15 Eng 24/25 EPL Relegation 200379       Aston Villa       101
#> 16 Eng 24/25 EPL Relegation 200386           Man Utd       151
#> 17 Eng 24/25 EPL Relegation 200390         Newcastle       151
#> 18 Eng 24/25 EPL Relegation 200397         Tottenham       251
#> 19 Eng 24/25 EPL Relegation 200404           Arsenal      2001
#> 20 Eng 24/25 EPL Relegation 200419         Liverpool      2001

Live (In-Play) Sports

To get live and in-play sports markets as they’re being played, the get_live_sports() function is to be used. This will return a data frame of all sports that have an in-play market.

The user can then filter the outputted data frame on any of the variables in the data set (name, displayName, competitions.id, competitions.matches.name, etc).

live_sports <- get_live_sports()
dplyr::glimpse(live_sports)

Racing Data

This sections will outline the functions available to extract Horse, Harness and Greyhound racing.

Past Racing Data

The following section will outline how to use functions do get racing data (horses, harness racing and greyhounds) for past races run.

This section does not include functions to get current day or future racing data.

Race Meet Meta Data

To get meta data for race meets for a selected date(s), use the get_race_meet_meta() function.

The output of this function will return where and when the meet(s) were, the weather and track condition, and high level data about each of the races at the meets.

dates <- seq(from = as.Date("2022-05-01"), to=as.Date("2022-05-03"), by=1)
race_meets <- get_race_meet_meta(race_dates=dates)

glimpse(race_meets)
#> Rows: 134
#> Columns: 11
#> $ meetingName      <chr> "BENDIGO", "SWAN HILL", "SANDOWN PARK", "HEALESVILLE"…
#> $ location         <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "NSW", "NSW…
#> $ venueMnemonic    <chr> "M", "M", "M", "E", "P", "P", "C", "C", "S", "S", "S"…
#> $ raceType         <chr> "R", "H", "G", "G", "R", "G", "R", "G", "R", "H", "G"…
#> $ meetingDate      <chr> "2022-05-01", "2022-05-01", "2022-05-01", "2022-05-01…
#> $ weatherCondition <chr> "FINE", "FINE", "OCAST", "OCAST", "FINE", "OCAST", "S…
#> $ trackCondition   <chr> "HVY8", "GOOD", "GOOD", "GOOD", "SOFT6", "GOOD", "HVY…
#> $ exoticPools      <list> [<data.frame[13 x 6]>], [<data.frame[12 x 6]>], [<da…
#> $ X_links          <df[,1]> <data.frame[26 x 1]>
#> $ races            <list> [<data.frame[8 x 10]>], [<data.frame[7 x 10]>], [<…
#> $ sellCode         <df[,2]> <data.frame[26 x 2]>

Loading Data

Rather than scraping this information, you can instead use the load_race_meet_meta() function. This will allow you to get a whole year’s worth of race meet meta data extremely quickly. This data updates daily, so should only very rarely not have the previous day’s race meet data:

loaded_race_meets <- load_race_meet_meta(cal_year=2022)
glimpse(loaded_race_meets)
#> Rows: 16,807
#> Columns: 11
#> $ meetingName      <chr> "FLEMINGTON", "ECHUCA", "THE MEADOWS", "MORNINGTON", …
#> $ location         <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "NSW", "NSW…
#> $ venueMnemonic    <chr> "M", "M", "M", "C", "E", "P", "L", "C", "C", "T", "S"…
#> $ raceType         <chr> "R", "H", "G", "R", "R", "G", "G", "R", "G", "G", "R"…
#> $ meetingDate      <chr> "2022-01-01", "2022-01-01", "2022-01-01", "2022-01-01…
#> $ weatherCondition <chr> "FINE", "FINE", "FINE", "FINE", "FINE", "OCAST", "FIN…
#> $ trackCondition   <chr> "GOOD4", "GOOD", "GOOD", "GOOD3", "FIRM2", "GOOD", "G…
#> $ exoticPools      <list> [<data.frame[14 x 6]>], [<data.frame[15 x 6]>], [<da…
#> $ X_links          <df[,1]> <data.frame[26 x 1]>
#> $ races            <list> [<data.frame[9 x 10]>], [<data.frame[10 x 10]>], […
#> $ sellCode         <df[,2]> <data.frame[26 x 2]>

Each Race Data

To get detailed data on runners, markets, pools and dividends, the following set of functions will be used.

Past Race data output

The get_past_races() function will allow you to pass in values for four parameters to have a list of races outputted. The result of this function should be stored for use with the parsing functions to be explained shortly.

The race_num value can be left off the function call to default to returning all races on a card’s meet.

The race_type value needs to be either ‘R’, ‘H’ or ‘G’.

The rest of the parameters can have multiple arguments passed to them.

race_list_output <- get_past_races(meet_date = c('2022-09-03', '2022-09-10'), venue_mnem = 'M', race_type = 'R', race_num = c(8:9))

Alternatively, you can get the same data as above using the TAB race API URL. To get this URL, follow the below steps:

# first load in meet data
meets <- load_race_meet_meta(cal_year = 2022)

# then filter to the race(s) meets wanted
meet_dates_df <- meets %>%
   filter(venueMnemonic == "M",
          raceType == "R",
          meetingDate == "2022-01-01")

# then get the URLs
meet_url <- meet_dates_df$races[[1]]
meet_url <- meet_url$`_links`$self[1]

# finally, running the function:
race_list_output <- get_past_race_content(urls = meet_url)

Parsing Race Data

So to easily get the data you want, parsing functions have been created to operate on the stored outputs of either the get_past_races() or get_past_race_content().

Parse runners and betting market

To get a race’s runners and betting odds, use the parse_runners() function:

runners <- parse_runners(race_list = race_list_output)
glimpse(runners)
#> Rows: 61
#> Columns: 34
#> $ meetingDate                 <chr> "2022-09-03", "2022-09-03", "2022-09-03", …
#> $ location                    <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", …
#> $ meetingName                 <chr> "MOONEE VALLEY", "MOONEE VALLEY", "MOONEE …
#> $ raceType                    <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R…
#> $ venueMnemonic               <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M…
#> $ raceNumber                  <chr> "8", "8", "8", "8", "8", "8", "8", "8", "8…
#> $ raceName                    <chr> "DRUMMOND GOLF HANDICAP", "DRUMMOND GOLF H…
#> $ raceStartTime               <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T0…
#> $ raceStatus                  <chr> "Paying", "Paying", "Paying", "Paying", "P…
#> $ raceDistance                <chr> "1600", "1600", "1600", "1600", "1600", "1…
#> $ resultedTime                <chr> "2022-09-03T06:53:24+00:00", "2022-09-03T0…
#> $ substitute                  <chr> "5", "5", "5", "5", "5", "5", "5", "5", "5…
#> $ raceClassConditions         <chr> "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "O…
#> $ winBook                     <chr> "http://internal-aviato-lb-212286492.ap-so…
#> $ scratchings                 <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1…
#> $ skyRacingAudio              <chr> "https://mediatabs.skyracing.com.au/Audio_…
#> $ runnerName                  <chr> "JOHNNY GET ANGRY", "DARK DREAM", "SO SI B…
#> $ runnerNumber                <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3…
#> $ finishingPosition           <int> 0, 0, 3, 0, 2, 1, 0, 0, 4, 0, 0, 0, 0, 2, …
#> $ trainerName                 <chr> "DENIS PAGAN", "BEN & JD HAYES", "BEN & JD…
#> $ barrierNumber               <int> 11, 2, 3, 6, 5, 7, 10, 4, 8, 1, 9, 9, 16, …
#> $ riderDriverName             <chr> "LACHLAN KING", "LAURA LAFFERTY", "LUKE NO…
#> $ claimAmount                 <dbl> -1.0, 3.0, -1.0, 2.0, -1.0, -1.0, -1.0, -1…
#> $ fixedOdds.returnWin         <dbl> 151.0, 31.0, 10.0, 23.0, 2.5, 3.4, 26.0, 2…
#> $ fixedOdds.returnWinOpen     <dbl> 81.0, 13.0, 12.0, 17.0, 3.0, 3.7, 13.0, 21…
#> $ fixedOdds.returnPlace       <dbl> 22.00, 6.00, 2.60, 4.60, 1.30, 1.45, 5.00,…
#> $ fixedOdds.bettingStatus     <chr> "LateScratched", "Loser", "Placing", "Lose…
#> $ fixedOdds.winDeduction      <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ fixedOdds.placeDeduction    <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ fixedOdds.propositionNumber <int> 159951, 159952, 159953, 159954, 159955, 15…
#> $ fixedOdds.scratchedTime     <chr> "2022-09-01T21:29:32+00:00", NA, NA, NA, N…
#> $ parimutuel.bettingStatus    <chr> "Scratched", "Normal", "Normal", "Normal",…
#> $ parimutuel.returnWin        <dbl> 0.0, 33.2, 10.1, 21.4, 2.5, 3.3, 21.2, 24.…
#> $ parimutuel.returnPlace      <dbl> 0.0, 6.3, 2.2, 4.7, 1.3, 1.5, 4.4, 5.2, 2.…

Parse pools

To get a race’s betting pools, use the parse_pools() function:

pools <- parse_pools(race_list = race_list_output)
glimpse(pools)
#> Rows: 108
#> Columns: 25
#> $ meetingDate         <chr> "2022-09-03", "2022-09-03", "2022-09-03", "2022-09…
#> $ location            <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "…
#> $ meetingName         <chr> "MOONEE VALLEY", "MOONEE VALLEY", "MOONEE VALLEY",…
#> $ raceType            <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", …
#> $ venueMnemonic       <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", …
#> $ raceNumber          <chr> "8", "8", "8", "8", "8", "8", "8", "8", "8", "8", …
#> $ raceName            <chr> "DRUMMOND GOLF HANDICAP", "DRUMMOND GOLF HANDICAP"…
#> $ raceStartTime       <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T06:50:00+…
#> $ raceStatus          <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ raceDistance        <chr> "1600", "1600", "1600", "1600", "1600", "1600", "1…
#> $ resultedTime        <chr> "2022-09-03T06:53:24+00:00", "2022-09-03T06:53:24+…
#> $ substitute          <chr> "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", …
#> $ raceClassConditions <chr> "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "O…
#> $ winBook             <chr> "http://internal-aviato-lb-212286492.ap-southeast-…
#> $ scratchings         <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", …
#> $ skyRacingAudio      <chr> "https://mediatabs.skyracing.com.au/Audio_Replay/2…
#> $ poolStatusCode      <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ wageringProduct     <chr> "OddsAndEvens", "Win", "Place", "Quinella", "Exact…
#> $ poolTotal           <dbl> 57.00, 199653.25, 72917.28, 41096.61, 14199.70, 39…
#> $ jackpot             <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ poolCloseTime       <chr> "2022-09-03T06:50:19+00:00", "2022-09-03T06:50:19+…
#> $ mergePool           <list> "Quinella", <NULL>, <NULL>, "OddsAndEvens", <NULL…
#> $ mergePoolTotal      <dbl> 41153.61, NA, NA, 41153.61, NA, NA, 93090.12, 9309…
#> $ legNumber           <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 1, 2, 1, 2,…
#> $ startTime           <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T06:50:00+…

Parse dividends

To get a race’s dividends, use the parse_dividends() function:

dividends <- parse_dividends(race_list = race_list_output)
glimpse(dividends)
#> Rows: 66
#> Columns: 24
#> $ meetingDate         <chr> "2022-09-03", "2022-09-03", "2022-09-03", "2022-09…
#> $ location            <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "…
#> $ meetingName         <chr> "MOONEE VALLEY", "MOONEE VALLEY", "MOONEE VALLEY",…
#> $ raceType            <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", …
#> $ venueMnemonic       <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", …
#> $ raceNumber          <chr> "8", "8", "8", "8", "8", "8", "8", "8", "8", "8", …
#> $ raceName            <chr> "DRUMMOND GOLF HANDICAP", "DRUMMOND GOLF HANDICAP"…
#> $ raceStartTime       <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T06:50:00+…
#> $ raceStatus          <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ raceDistance        <chr> "1600", "1600", "1600", "1600", "1600", "1600", "1…
#> $ resultedTime        <chr> "2022-09-03T06:53:24+00:00", "2022-09-03T06:53:24+…
#> $ substitute          <chr> "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", …
#> $ raceClassConditions <chr> "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "O…
#> $ winBook             <chr> "http://internal-aviato-lb-212286492.ap-southeast-…
#> $ scratchings         <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", …
#> $ skyRacingAudio      <chr> "https://mediatabs.skyracing.com.au/Audio_Replay/2…
#> $ poolStatusCode      <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ wageringProduct     <chr> "OddsAndEvens", "Win", "Place", "Place", "Place", …
#> $ jackpotCarriedOver  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ poolCloseTime       <chr> "2022-09-03T06:50:19+00:00", "2022-09-03T06:50:19+…
#> $ mergePool           <list> "Quinella", <NULL>, <NULL>, <NULL>, <NULL>, "Odds…
#> $ mergePoolTotal      <dbl> 41153.61, NA, NA, NA, NA, 41153.61, NA, NA, NA, NA…
#> $ selections          <list> "SPLIT", 6, 6, 5, 3, <6, 5>, <6, 5>, <6, 5>, <6, …
#> $ amount              <dbl> 1.5, 3.3, 1.5, 1.3, 2.2, 3.6, 6.8, 2.1, 5.0, 4.2, …