Bet Tailing Bot: Reverse Engineering Sports Betting APIs

Profile picture
Lamarr
13 min readAugust 6, 2024

DISCLAIMER: This was for research purposes only. Do not use this code/approach for anything real :)

sports betting

Sports Betting

As states continue to legalize sports betting, sportsbooks like FanDuel, DraftKings, and Bet365 are rising in popularity. This has led to the emergence of many sports cappers who sell their picks to other gamblers for “tailing.” This is usually done through a subscription model where users pay a set amount per month to access all of the capper’s picks.

fanduel-slipExample NBA Bet

There can be dozens of slips placed over the course of a month. Often, bets are placed right before games, making it difficult for people to tail all of them. It’s like paying Warren Buffet $50 a month to access all his stock trades; if he makes a last-minute decision to sell, you may not act quickly enough. Both sports betting and stock trading share this problem. Automating the process seems like a potential solution to ensure you always place the same bets as your favorite capper.

Can You Automate Sports Betting

To determine if this task can be automated, we first need to understand how people do it manually.

Manually Tailing Picks

The general workflow is that cappers post their picks, notify their users, and then their users tail the picks.

  1. Posting Picks: Most cappers post screenshots of their picks in Discord servers. Others may automatically track their picks on trackers like Action Network.
  2. Notifications: Users are commonly notified on their phones.
  3. Tailing the Bet: Users choose the sportsbook of their choice, such as FanDuel, Bet365 or Draft Kings. Then, they place the same bet as their capper.

These steps may seem simple to automate, but there are surprising challenges that arise when going down that path.

Getting The Picks

Discord Pick Screenshots

Cappers post picks in their Discord servers as screenshots. While Discord exposes APIs for applications/bots to get messages in a channel, it doesn’t expose this functionality for users. Thus, you would need your capper to install your bot with admin-like permissions, which is unlikely. However, you can use your user token to get messages in a channel, as detailed in this blog by Keshav Kalra

Some sample code to get messages would look like this:

1import requests 2 3USER_TOKEN = 'your-user-token' 4CHANNEL_ID = 'your-channel-id' 5 6headers = { 7 'Authorization': USER_TOKEN, 8 'Content-Type': 'application/json' 9} 10 11def get_messages(channel_id): 12 url = f'https://discord.com/api/v9/channels/{channel_id}/messages' 13 response = requests.get(url, headers=headers) 14 if response.status_code == 200: 15 messages = response.json() 16 for message in messages: 17 print(f'{message["author"]["username"]}: {message["content"]}') 18 else: 19 print(f'Failed to retrieve messages: {response.status_code}') 20 21get_messages(CHANNEL_ID)

Then you would need a way to get the text from the screenshots. We can do this using a few python libraries. Pillow is useful for parsing the image into a usable format from the Discord API. Next, we can use PyTesseract for its OCR capabilities, allowing us to extract the text from the screenshot.

1from PIL import Image 2import pytesseract 3import xml.etree.ElementTree as ET 4 5def png_to_pdf(): 6 pdf = pytesseract.image_to_pdf_or_hocr("discord_screenshot.png", extension="pdf") 7 with open("discord_screenshot.pdf", "w+b") as f: 8 f.write(pdf) 9 10def get_text_from_pdf(): 11 text = pytesseract.image_to_string(Image.open("discord_screenshot.png")) 12 pdf = pytesseract.image_to_pdf_or_hocr("test.png", extension="pdf") 13 with open("test.pdf", "w+b") as f: 14 f.write(pdf) 15 return text 16 17def get_xml_from_pdf(): 18 xml = pytesseract.image_to_alto_xml("discord_screenshot.pdf") 19 tree = ET.parse("discord_screenshot.xml") 20 return tree

This approach would allow us to turn the example parlay above into structured data that we can use later:

1[ 2 { 3 "sport": "Basketball", 4 "team1": "USA", 5 "team2": "Serbia", 6 "prop": "LeBron James to score 20+ points", 7 "player": "LeBron James" 8 }, 9 { 10 "sport": "Basketball", 11 "team1": "USA", 12 "team2": "Serbia", 13 "prop": "Nikola Jokic to score 20+ points", 14 "player": "Nikola Jokic" 15 }, 16 { 17 "sport": "Basketball", 18 "team1": "USA", 19 "team2": "Serbia", 20 "prop": "Anthony Davis over 7.5 rebounds", 21 "player": "Anthony Davis" 22 }, 23 ... 24]

Action Network Picks

action-networkExample action network slip

On the surface the action network path seems simpler as we don't have to parse text from images. However, Action Network doesn't expose any public APIs for accessing picks. Even worse, the Action Network bet tracker is only available as a mobile or desktop app. So, we can't easily check the APIs that are called from our browser. We’ll need to get creative to access this data.

Network Sniffing

Since there isn't a website available, let’s install the Action Network App. While we can’t check the Browser Network tab for API requests, we can intercept traffic using tools like WireShark and Fiddler. Let’s use Fiddler to find the Action Network APIs.

fiddler-1Fiddler Main Screen

Using System Proxy in Live Mode captures all traffic from our computer, including apps.

fiddler-2Fiddler Trace

We can see the app uses an API in the format of api.actionnetwork.com/mobile/v1/users/<user_id>/profile. This endpoint returns all of a users bets! So, we can use a script that periodically checks if a capper has made new bets for us to tail.

1def get_action_bets(): 2 bets = [] 3 capper_user_id = "123456" 4 url = f"https://api.actionnetwork.com/web/v1/users/{capper_user_id}/profile" 5 headers = {} 6 7 response = requests.get(url, headers=headers) 8 picks = response.json()["picks"]

Regardless of whether our capper uses Discord or Action Network, we can get their picks programmatically!

Tailing

The next challenge is automating the bet placement. We could use selenium on AWS for this. However, most sportsbooks have locality checks via separate apps. Potential solutions include:

  • Using a sportsbook that doesn’t verify location through apps.
  • Running the automation on localized hardware. For example, to use FanDuel in Ohio, you could buy a reserved EC2 instance in us-east-2 (Ohio) and run the automation from there, passing the location check.

These workarounds enable using data collected from cappers on Discord/Action Network to place bets automatically.

Parting Thoughts

Building an automated betting solution powered by top cappers is technically possible. However, automated bets likely violate the TOS of most sportsbooks. Thus, this remains a research project.

This project touched on many tools that deserve in-depth posts of their own. PyTesseract is powerful for OCR, and network sniffing with Fiddler has many security applications. Gaining experience with these tools on a real-world problem was a lot of fun!