An Amazon price tracker Telegram bot needs three parts: the Telegram Bot API to receive commands and send messages, a product lookup that returns the current price, and a schedule that compares prices. The bot below does it in one Python file: /track B0CL61F39H starts tracking, every six hours it looks each product up with /api/amazon-product-lookup, and it messages you when the price falls. An allow-list of chat IDs keeps strangers from spending your credits.
Key Takeaways
- Telegram's Bot API is plain HTTPS: getUpdates with a long-poll timeout receives commands, sendMessage answers.
- Pass offset = last update_id + 1 to getUpdates, or Telegram sends the same commands again.
- Anyone can message a bot: accept commands only from allow-listed chat IDs, because every lookup costs a credit.
- Validate the ASIN before looking it up - a malformed ASIN is sent and billed like any other request.
- Checking every six hours costs about 120 credits per tracked ASIN a month.
What do I need before I start?
- A bot token from Telegram's @BotFather, and your chat ID for the allow-list.
- A SellerMagnet API key - a free account includes 150 credits.
- Python 3.10+,
requests, and thesellermagnet.pyhelper from the Python quickstart.
Configuration through environment variables
export TELEGRAM_BOT_TOKEN="123456:your-bot-token"
export TELEGRAM_ALLOWED_CHATS="123456789" # comma-separated chat IDs allowed to use the bot
export SELLERMAGNET_API_KEY="your-key-here"
export MARKETPLACE_ID="ATVPDKIKX0DER" # amazon.com; A1PA6795UKMFR9 for amazon.de
.venv/bin/python price_bot.py
How does the bot receive commands?
With long polling: getUpdates waits up to 50 seconds for new messages and returns them as updates. Every request goes to https://api.telegram.org/bot<token>/METHOD, and every answer is JSON with an ok flag: result on success, description and error_code on failure. Passing offset one above the last update_id confirms the updates you have handled, so Telegram does not deliver them again. No web server, domain or webhook is needed - and if the token was used with a webhook before, getUpdates answers 409 Conflict until you call deleteWebhook.
| Command | Does | Cost |
|---|---|---|
/track ASIN | Looks the product up, starts tracking | 1 credit |
/untrack ASIN | Stops tracking | free |
/list | Tracked products with last price | free |
| Every 6 hours | Re-checks each tracked ASIN | 1 credit per ASIN |
How do I build the Amazon price tracker bot?
handle() answers commands, check_prices() compares every tracked product with its last price and messages the chat on a drop, and main() runs the polling loop, retries after network or Telegram errors without printing the token, and saves the state after each round. The price comes from buyBoxInfo.price; "N/A" - no featured offer - never counts as zero, and the bot keeps the last known price. A delisted product (404) is dropped, because each check of it is billed, and a rejected key or empty credit balance is reported instead of failing silently. To accept Amazon links as well as ASINs, add the parser from the ASIN-from-URL guide.
price_bot.py - the complete bot
"""Telegram bot that tracks Amazon prices: /track ASIN, /list, /untrack ASIN."""
import json
import os
import re
import time
from pathlib import Path
import requests
from sellermagnet import ApiError, get, new_session
TG = f"https://api.telegram.org/bot{os.environ['TELEGRAM_BOT_TOKEN']}"
ALLOWED = {int(c) for c in os.environ["TELEGRAM_ALLOWED_CHATS"].split(",")} # every lookup spends your credits
MARKETPLACE = os.environ.get("MARKETPLACE_ID", "ATVPDKIKX0DER")
CHECK_EVERY = 6 * 3600 # 4 checks a day = about 120 credits per tracked ASIN a month
STATE = Path("tracked.json")
ASIN = re.compile(r"^(?:B[0-9A-Z]{9}|[0-9]{9}[0-9X])$")
def tg(method: str, **params):
body = requests.post(f"{TG}/{method}", json=params, timeout=70).json()
if not body.get("ok"):
raise RuntimeError(f"Telegram {method}: {body.get('description')}")
return body["result"]
def price(asin: str) -> dict:
info = get(new_session(), "amazon-product-lookup", asin=asin, marketplaceId=MARKETPLACE)["productInfo"]
p = info["buyBoxInfo"].get("price")
return {"price": p if isinstance(p, (int, float)) else None, # "N/A" = no featured offer
"currency": info["buyBoxInfo"].get("currencyCode") or "",
"title": (info.get("title") or asin)[:60], "link": info.get("link")}
def shown(item: dict) -> str:
return "no featured offer" if item["price"] is None else f"{item['price']:.2f} {item.get('currency', '')}".strip()
def handle(text: str, chat: str, state: dict) -> str:
cmd, _, arg = text.strip().partition(" ")
cmd = cmd.split("@")[0] # "/track@YourBot" in group chats
tracked = state.setdefault(chat, {})
if cmd == "/list":
lines = [f"{a}: {shown(v)} - {v['title']}" for a, v in tracked.items()]
return "\n".join(lines)[:4000] or "Nothing tracked yet." # Telegram allows 4,096 characters
asin = arg.strip().upper()
if cmd in ("/track", "/untrack") and not ASIN.match(asin):
return "Send an ASIN, e.g. /track B0CL61F39H" # checked before any credit is spent
if cmd == "/untrack":
return "Stopped tracking." if tracked.pop(asin, None) else "Not tracked."
if cmd == "/track":
try:
item = price(asin)
except ApiError as err:
return f"Lookup failed ({err.status}): {asin} on this marketplace?"
except requests.RequestException:
return "Lookup timed out - try /track again later."
tracked[asin] = item
return f"Tracking {item['title']}: {shown(item)}"
return "Commands: /track ASIN, /untrack ASIN, /list"
def check_prices(state: dict) -> None:
for chat, tracked in state.items():
for asin, item in list(tracked.items()):
try:
now = price(asin)["price"]
except ApiError as err:
if err.status == 404: # billed on every check: stop tracking it
tracked.pop(asin)
tg("sendMessage", chat_id=int(chat), text=f"{asin} is no longer listed - stopped tracking.")
elif err.status == 401: # key rejected or credits used up
tg("sendMessage", chat_id=int(chat), text="SellerMagnet rejected the key or credits ran out.")
return
continue
except requests.RequestException:
continue # try again at the next check
if now is not None and item["price"] is not None and now < item["price"]:
old = shown(item)
item["price"] = now
tg("sendMessage", chat_id=int(chat),
text=f"Price drop: {item['title']}\n{old} -> {shown(item)}\n{item.get('link') or asin}")
elif now is not None:
item["price"] = now # "N/A" keeps the last known price
def main() -> None:
state = json.loads(STATE.read_text()) if STATE.exists() else {}
offset, last_check = 0, time.time() # first price check one interval after start
while True:
try:
for update in tg("getUpdates", offset=offset, timeout=50, allowed_updates=["message"]):
offset = update["update_id"] + 1 # confirms this update
msg = update.get("message") or {}
chat_id = (msg.get("chat") or {}).get("id")
if chat_id in ALLOWED and msg.get("text"):
tg("sendMessage", chat_id=chat_id, text=handle(msg["text"], str(chat_id), state))
if time.time() - last_check >= CHECK_EVERY:
last_check = time.time() # set first: a failed round is not re-run at once
check_prices(state)
except (requests.RequestException, ValueError, RuntimeError) as err: # network, 5xx page, 409, 429
print("retrying in 30 s:", str(err).replace(os.environ["TELEGRAM_BOT_TOKEN"], "<token>"))
time.sleep(30)
STATE.write_text(json.dumps(state, indent=1))
if __name__ == "__main__":
main()
A chat with the bot (example values)
/track B0CL61F39H
Tracking PS5 slim: 444.99 USD
/list
B0CL61F39H: 444.99 USD - PS5 slim
Price drop: PS5 slim
444.99 USD -> 419.99 USD
https://www.amazon.com/dp/B0CL61F39H

Protect your credits
A Telegram bot is public: anyone who finds its name can message it. Without the allow-list, a stranger could track hundreds of ASINs on your account. Keep TELEGRAM_ALLOWED_CHATS to your own chat IDs - allow-listing a group (a negative ID) lets every member spend credits - and keep the bot token out of your repository.
How often should the bot check prices?
Each check is one lookup per tracked ASIN; the first check runs one interval after the bot starts, and tracking an ASIN again costs another lookup. Every six hours is about 120 credits per ASIN a month and catches most price moves within a quarter of a day; daily checks cost 30. The same checks see a product come back after "N/A" - the out-of-stock alert guide turns that into its own alert. For products where minutes matter, the Buy Box polling guide weighs shorter intervals, and the price history guide shows whether today's price is really low.

Frequently Asked Questions
Can a Telegram bot track Amazon prices?
Yes. The bot receives /track commands through the Telegram Bot API, looks the product up on a schedule and sends a message when the price falls.
Do I need a server with a public URL?
No. Long polling with getUpdates works from any machine with internet access; a webhook is optional.
Why restrict the bot to certain chat IDs?
Anyone can message a public bot, and every lookup costs a credit. The allow-list keeps your credits for your own chats.
What happens when a product has no featured offer?
The lookup returns "N/A" as the price. The bot keeps the last known price and alerts only when both the old and the new price are known.
How much does tracking cost?
One credit when you start tracking and one per check per product. Every six hours is about 120 credits per product a month.
Bottom line: one Python file, the Telegram Bot API and a product lookup make a private price tracker - long polling for commands, a check every few hours, and an allow-list that keeps the credits yours. Every lookup field is on the product lookup page.