Home / Blog / Build an Amazon Price Tracker Telegram Bot in Python

Build an Amazon Price Tracker Telegram Bot in Python

Build a Telegram bot that tracks Amazon prices: /track an ASIN, get a message when the price drops. Long polling with the Telegram Bot API, product lookups from the SellerMagnet API, an allow-list so strangers cannot spend your credits, and one small Python file.

September 27, 2026
4 min read
SellerMagnet Team
Share & Bookmark
Blueprint flow from a Telegram chat through a Python bot and product lookups to a price drop message

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 the sellermagnet.py helper 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.

The bot's commands and what each one costs.
CommandDoesCost
/track ASINLooks the product up, starts tracking1 credit
/untrack ASINStops trackingfree
/listTracked products with last pricefree
Every 6 hoursRe-checks each tracked ASIN1 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
Blueprint table of the bot's commands, what each one does and what it costs in credits
Only /track and the scheduled checks cost credits.

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.

Blueprint bar chart of credits per tracked ASIN per month for four check intervals
Credits per tracked ASIN per month.

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.

Ready to Extract Amazon Data at Scale?

Start building with SellerMagnet API today. Real-time product data, competitive pricing, and review analytics at your fingertips.

150 free API credits • No credit card required • Cancel anytime