Home › Guides › How to build a Telegram odds bot
Updated September 25, 2026 · facts checked against the dated sources listed below
An odds bot needs three pieces: a token from Telegram's BotFather, a loop that asks Telegram for new messages, and a data source to answer from. This guide builds one in 65 lines of standard-library Python that replies to /odds fed december with matching Polymarket markets from this site's free JSON API, credit included.
In Telegram, open a chat with @BotFather and send /newbot. It asks for a display name and a username, then replies with an authentication token. Telegram's documentation warns that anyone who has the token can control your bot, so keep it out of your code and out of version control: the script reads it from an environment variable named TELEGRAM_BOT_TOKEN. If the token leaks, BotFather's /token command generates a new one.
Telegram offers two ways to receive messages, and a bot can use only one at a time. With long polling, your script calls getUpdates with a timeout and Telegram holds the request open until something arrives; each call passes an offset one higher than the last update_id you handled, which tells Telegram the earlier updates are done. With a webhook, set with setWebhook, Telegram sends each update to your HTTPS address on port 443, 80, 88 or 8443. Either way, updates wait on Telegram's servers for up to 24 hours.
Polling works from a laptop or any machine without a public address, so it suits a small bot; a webhook needs a public HTTPS endpoint but spares you an open connection. The script below polls.
Save the script as odds_bot.py, set the token in your shell and start it, then message your bot /odds fed december or /odds recession. It uses only Python's standard library: urllib.request for HTTP, urllib.parse to encode the parameters and json to read the replies.
import json
import os
import time
import urllib.parse
import urllib.request
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
TELEGRAM = "https://api.telegram.org/bot" + TOKEN + "/"
ODDS_INDEX = "https://polymarkettrader.com/api/v1/odds/index.json"
HEADERS = {"User-Agent": "my-odds-bot/1.0"}
CACHE_SECONDS = 1800
cache = {"data": None, "time": 0.0}
def call(url, params=None, timeout=30):
if params:
url += "?" + urllib.parse.urlencode(params)
request = urllib.request.Request(url, headers=HEADERS)
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.load(response)
def odds_index():
if cache["data"] is None or time.time() - cache["time"] > CACHE_SECONDS:
cache["data"] = call(ODDS_INDEX)
cache["time"] = time.time()
return cache["data"]
def answer(query):
words = query.lower().split()
if not words:
return "Send /odds and a few words, for example: /odds fed december"
data = odds_index()
hits = [m for m in data["markets"] if all(w in m["title"].lower() for w in words)]
if not hits:
return "No tracked market matches: " + " ".join(words)
lines = [f'{m["title"]}\n{m["leader"]}: {m["probability"]:.1%}\n{m["url"]}' for m in hits[:3]]
built = data["updated"][:16].replace("T", " ")
lines.append(f'{data["attribution"]["text"]} {data["attribution"]["url"]} ({data["license"]}), updated {built} UTC')
return "\n\n".join(lines)
def main():
offset = None
while True:
try:
params = {"timeout": 50}
if offset is not None:
params["offset"] = offset
updates = call(TELEGRAM + "getUpdates", params, timeout=60)
for update in updates["result"]:
offset = update["update_id"] + 1
message = update.get("message") or {}
command, _, query = message.get("text", "").partition(" ")
if command.split("@")[0] in ("/odds", "/start"):
reply = answer(query)
call(TELEGRAM + "sendMessage", {"chat_id": message["chat"]["id"], "text": reply})
except Exception as error:
print("error:", error)
time.sleep(5)
if __name__ == "__main__":
main()export TELEGRAM_BOT_TOKEN="paste-the-token-from-BotFather-here"
python3 odds_bot.py
answer() searches the titles in /api/v1/odds/index.json for every word you typed and returns up to three matches, each with its leading outcome, probability and a link to the market's page on this site. It reads only fields the index publishes: title, leader, probability and url for each market, plus updated, license and the attribution text and url for the credit line. Matching is deliberately simple: every word must appear in the title, so /odds bitcoin september narrows the list faster than /odds bitcoin. Three matches stay far below Telegram's limit of 4,096 characters per message.
odds_index() keeps the file in memory for 30 minutes. The data changes only about twice a day, so downloading it for every message would add load without adding freshness. The script also sends its own User-Agent, because our CDN rejects Python's default one.
See also: How to use the free odds API
Every reply ends with the credit line, link and license name from the file and the time the data was built; CC BY 4.0 also asks for a link to the license itself, which you can put in the bot's description or its /start reply. Telegram's FAQ asks bots to avoid sending more than one message per second in a single chat and says a bot cannot send more than 20 messages per minute to a group, so answer on demand or batch your alerts. A command can also arrive with the bot's username attached, as /odds@YourBotName, the form of the Bot API's own example /start@jobs_bot; the script drops the part after the @ so both forms work.
To turn it into an alert bot, save each market's probability on every refresh and message a chat when one moves by more than a threshold you pick, such as 5 points. Before sharing it widely, add logging, run it under a process manager and never paste the token into a chat or a repository.
See also: How to build a prediction market watchlist · Free odds API and endpoints
No. The Bot API takes plain HTTPS requests and returns JSON, so the standard library is enough for a small bot; a library can add conveniences as it grows.
In an environment variable or a secrets manager, never in the code. Anyone with the token controls the bot, and BotFather can generate a new one if it leaks.
At the time of writing, our CDN rejects requests that identify as Python-urllib, the default of Python's standard library. Send your own User-Agent header, as the script does.
The API is rebuilt about twice a day and the bot caches it for 30 minutes, so a reply can be several hours old; each reply shows when the data was built.
Polymarket View is independent and not affiliated with Polymarket. Educational information, not financial advice. All guides · How market types work · Glossary · Polymarket odds today