How to Build a 24/7 Competitor Price Tracking Bot with Python and Telegram Alerts

How to Build a 24/7 Competitor Price Tracking Bot with Python and Telegram Alerts 2026 Guide

In the hyper-competitive world of e-commerce, retail arbitrage, and digital direct-to-consumer (DTC) brands, pricing agility is the primary driver of gross margin and market share. When a primary competitor quietly drops their retail price by 15%, launches a weekend flash promotion, or runs out of stock, taking 48 hours to discover the change means burning valuable ad spend or losing thousands of sales to the competition.

Commercial competitive intelligence platforms like Prisync and Competera charge upwards of $500 to $2,000 monthly for price monitoring, while restricting scan frequencies to once or twice daily. For technical growth teams and agile merchants, building an autonomous competitor price tracking bot using Python, Playwright, SQLite, and the Telegram Bot API delivers sub-minute price drop notifications with complete customization and zero monthly SaaS software fees.

Direct Answer: What Is the Ideal Python Price Tracking Architecture?

A production-grade competitor price tracking bot operates across four distinct technical stages:

1. Headless Browser Extraction (Playwright): Executes JavaScript, waits for dynamic DOM hydration, and extracts pricing data from rendered CSS selectors or embedded schema.org/Product JSON-LD microdata.
2. Anti-Detection Stealth Layer: Rotates residential proxies, masks navigator webdriver signatures, and injects realistic browser fingerprints to bypass Cloudflare Turnstile and DataDome WAFs.
3. Historical Price Ledger (SQLite / PostgreSQL): Records timestamped price observations, calculates historical moving averages, and computes percentage price deltas.
4. Instant Dispatch (Telegram Bot API): When a price changes by more than a specified threshold (e.g., >5%), the bot sends a rich Markdown message directly to your phone or team channel containing product image, old price, new price, and purchase link.

Why Naive requests and BeautifulSoup Fail in 2026

A decade ago, scraping e-commerce websites required little more than requests.get(url) paired with BeautifulSoup. Today, nearly all major e-commerce platforms (Shopify Plus, WooCommerce, Amazon, Magento, and custom Next.js/React storefronts) deploy aggressive client-side rendering and bot protection engines.

Attempting to scrape modern storefronts with basic HTTP libraries triggers three immediate roadblocks:

  • Blank HTML Payloads (SPA Hydration): The initial server response returns empty <div id="root"></div> containers. Prices are loaded asynchronously via client-side GraphQL or REST API requests that never execute without a real JavaScript runtime.
  • Web Application Firewall (WAF) Fingerprinting: Cloudflare, Akamai, and DataDome analyze TLS handshakes (JA3/JA4 fingerprints) and HTTP/2 settings. Python’s default urllib and requests TLS fingerprints are immediately flagged, resulting in HTTP 403 Forbidden errors or Cloudflare Turnstile challenge loops.
  • Behavioral Bot Surveillance: Modern storefronts inspect mouse movement, canvas rendering, WebGL context, and touch event listeners. Headless browsers lacking stealth patches expose navigator.webdriver = true, triggering silent IP bans.

The 4-Layer Autonomous Price Tracking Engine

To overcome anti-scraping barriers and run reliably 24 hours a day, 7 days a week, our architecture utilizes Playwright with stealth configurations.

Layer 1: Headless Playwright with Browser Stealth

Playwright launches real Chromium, Firefox, or WebKit browser binaries, evaluating complete JavaScript bundles exactly like a human user. By applying stealth overrides, we mask the automated runtime:

# Masking webdriver signals in Playwright
await page.add_init_script("""
    delete Object.getPrototypeOf(navigator).webdriver;
    window.chrome = { runtime: {} };
""")

Layer 2: Robust Data Extraction (CSS Selectors & JSON-LD)

Rather than relying solely on fragile CSS class names that change during competitor frontend deployments, our bot extracts pricing using a two-pronged fallback approach:

  1. JSON-LD Schema Extraction: Nearly all modern e-commerce stores inject standardized structured data for Google Shopping: <script type="application/ld+json">. Extracting offers.price and offers.priceCurrency guarantees 99.9% parsing accuracy regardless of visual CSS changes.
  2. Targeted CSS Fallback: If structured data is absent, the scraper evaluates robust selector queries (e.g., [data-product-price], .price-item--regular, meta[property="og:price:amount"]).

Layer 3: SQLite Price History Database Schema

To detect real price drops and filter out temporary currency conversion fluctuations, the bot records every observation in a lightweight local SQLite database:

SQLite Price History Schema (schema.sql)

CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    sku TEXT UNIQUE,
    title TEXT,
    competitor_name TEXT,
    url TEXT,
    target_price REAL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS price_history (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    product_id INTEGER,
    price REAL,
    in_stock BOOLEAN,
    recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(product_id) REFERENCES products(id)
);

Layer 4: Real-Time Telegram Bot Notification Dispatch

When the scraped price differs from the last recorded price in SQLite, the bot calculates the percentage difference. If the price drop exceeds your defined alert threshold, it constructs an interactive Telegram push notification via the official Telegram Bot API.

Complete Production Python Script: 24/7 Price Sentinel

Below is the complete, standalone Python implementation. It initializes SQLite, runs Playwright asynchronously, inspects pricing, updates history, and dispatches rich Telegram alerts.

Production Price Tracking Bot (price_sentinel.py)

import asyncio
import json
import os
import re
import sqlite3
import aiohttp
from playwright.async_api import async_playwright

TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")
DB_FILE = "competitor_prices.db"

def init_db():
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute("""
        CREATE TABLE IF NOT EXISTS products (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            sku TEXT UNIQUE,
            title TEXT,
            url TEXT,
            competitor TEXT
        )
    """)
    c.execute("""
        CREATE TABLE IF NOT EXISTS price_logs (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            product_id INTEGER,
            price REAL,
            in_stock INTEGER,
            checked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    """)
    conn.commit()
    conn.close()

async def send_telegram_alert(title, competitor, old_price, new_price, url):
    pct_change = ((new_price - old_price) / old_price) * 100
    emoji = "๐Ÿšจ PRICE DROP ALERT!" if pct_change < 0 else "๐Ÿ“ˆ PRICE INCREASE"
    
    msg = (
        f"*{emoji}*\n\n"
        f"๐Ÿ“ฆ *Product:* {title}\n"
        f"๐Ÿข *Competitor:* {competitor}\n"
        f"๐Ÿ’ฐ *Old Price:* ${old_price:.2f}\n"
        f"๐Ÿ”ฅ *New Price:* ${new_price:.2f} ({pct_change:+.1f}%)\n\n"
        f"๐Ÿ”— [View Competitor Storefront]({url})"
    )
    
    endpoint = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    payload = {
        "chat_id": TELEGRAM_CHAT_ID,
        "text": msg,
        "parse_mode": "Markdown",
        "disable_web_page_preview": False
    }
    async with aiohttp.ClientSession() as session:
        await session.post(endpoint, json=payload, timeout=10)

async def scrape_product(page, url):
    await page.goto(url, wait_until="domcontentloaded", timeout=30000)
    await page.wait_for_timeout(2000)
    
    scripts = await page.query_selector_all('script[type="application/ld+json"]')
    for s in scripts:
        try:
            content = await s.inner_text()
            data = json.loads(content)
            items = data if isinstance(data, list) else [data]
            for item in items:
                if item.get("@type") == "Product" and "offers" in item:
                    offers = item["offers"]
                    price = offers.get("price") or (offers[0].get("price") if isinstance(offers, list) else None)
                    if price:
                        return float(re.sub(r"[^\d.]", "", str(price))), True
        except Exception:
            continue
            
    price_selectors = [
        '[data-product-price]',
        '.price-item--sale',
        '.price-item--regular',
        'span.price',
        '.woocommerce-Price-amount'
    ]
    for sel in price_selectors:
        el = await page.query_selector(sel)
        if el:
            txt = await el.inner_text()
            clean_price = re.sub(r"[^\d.]", "", txt)
            if clean_price:
                return float(clean_price), True
                
    return None, False

async def monitor_cycle():
    init_db()
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    
    c.execute("SELECT id, sku, title, url, competitor FROM products")
    products = c.fetchall()
    
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context(
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        )
        page = await context.new_page()
        
        for pid, sku, title, url, comp in products:
            price, in_stock = await scrape_product(page, url)
            if price is not None:
                c.execute("SELECT price FROM price_logs WHERE product_id = ? ORDER BY checked_at DESC LIMIT 1", (pid,))
                last_row = c.fetchone()
                
                if last_row and last_row[0] > 0:
                    old_price = last_row[0]
                    if abs(price - old_price) >= (old_price * 0.02):
                        await send_telegram_alert(title, comp, old_price, price, url)
                        
                c.execute("INSERT INTO price_logs (product_id, price, in_stock) VALUES (?, ?, ?)", (pid, price, int(in_stock)))
                conn.commit()
                
            await asyncio.sleep(3)
            
        await browser.close()
    conn.close()

if __name__ == "__main__":
    asyncio.run(monitor_cycle())

Bypassing Advanced Anti-Bot Defenses: Cloudflare Turnstile & DataDome

When tracking heavily defended retail giants (such as Best Buy, Target, or Nike), simple User-Agent spoofing is insufficient. Advanced scraping pipelines deploy three specialized evasion tactics:

Enterprise Evasion Strategies

  • Rotating Residential Proxies: Never scrape from data center IP ranges (AWS, DigitalOcean, Hetzner), which are blocked by default. Route requests through residential proxy providers (Bright Data, Smartproxy, Oxylabs) where exit nodes originate from residential ISP connections (Comcast, AT&T, Vodafone).
  • Playwright Stealth Plugin: Utilize playwright-stealth to override hundreds of browser fingerprinting parameters, including WebGL vendor strings, canvas noise, battery API status, and audio context frequencies.
  • Randomized Viewports & Human Interaction: Randomize browser window dimensions (e.g., 1920×1080 vs 1440×900) and simulate realistic mouse hover paths and smooth page scrolling before reading DOM properties.

Production Deployment: Running 24/7 via Docker & Systemd

To run your competitor price tracking bot autonomously around the clock without keeping a laptop open, deploy the script inside a Docker container on an affordable $5/month cloud VPS (Ubuntu 24.04 LTS on Hetzner or DigitalOcean).

Dockerfile Configuration

FROM mcr.microsoft.com/playwright/python:v1.45.0-jammy

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN playwright install chromium

COPY . .
CMD ["python", "-u", "price_sentinel.py"]

Systemd Scheduling via Cron (Every 30 Minutes)

Configure a Linux cron schedule to invoke the container every 30 minutes, recording price movements and sending alerts while you sleep:

*/30 * * * * cd /opt/price-sentinel && python3 price_sentinel.py >> /var/log/price_tracker.log 2>&1

Legal, Ethical & Rate-Limiting Guidelines

Public pricing information displayed openly on e-commerce storefronts is generally considered public factual data. However, commercial scrapers must adhere to responsible technical standards:

  • Respect Server Load: Never hammer a competitor’s storefront with concurrent threads. Maintain a 3 to 5-second sleep interval between sequential requests to ensure zero impact on their server capacity.
  • Scrape Product Pages, Not Cart Workflows: Restrict crawling strictly to public product detail pages. Never automate checkout flows, inventory hoarding, or fake cart reservation tactics.
  • Honor robots.txt Guidelines: Inspect the competitor’s robots.txt file to avoid crawling internal admin or search query parameter endpoints.

To explore more automated competitive intelligence blueprints and e-commerce growth workflows, explore our guides in the Automation Hub and schedule an Operations Tech Stack Audit with our technical architecture team.

Proxy Pool Engineering: Residential vs Mobile 4G/5G Backconnect Gateways

When tracking hundreds of competitor SKUs daily across sophisticated anti-scraping storefronts, IP reputation is your primary operational bottleneck. Scraping from fixed cloud hosting IPs (such as AWS, DigitalOcean, or Linode) will result in immediate CAPTCHA challenges or silent IP rate-limiting.

Proxy Tiers Compared for Price Scraping

  • Datacenter Proxies (Lowest Cost, Highest Ban Rate): Fast and inexpensive ($0.50-$1.00/GB), but ASN ranges are openly identified as commercial datacenters. Suitable only for small local Shopify stores lacking Cloudflare Bot Management.
  • Rotating Residential Proxies (The Industry Standard): Routed through real home Wi-Fi broadband connections (Comcast, Charter, Verizon). Cost ranges from $4 to $8 per GB. Because the exit node IP belongs to a legitimate consumer ISP, anti-bot engines cannot ban the IP without blocking real customers.
  • Mobile 4G/5G Proxies (Maximum Stealth): Mobile cellular carriers (Verizon, T-Mobile, Vodafone) use Carrier-Grade NAT (CGNAT) where thousands of legitimate mobile smartphone users share a single public cellular IP address. Anti-bot systems cannot ban a mobile IP address without blocking thousands of active mobile shoppers.

Configuring Playwright with Backconnect Proxy Authentication

# Authenticated Residential Proxy Integration
browser = await p.chromium.launch(
    headless=True,
    proxy={
        "server": "http://pr.oxylabs.io:7777",
        "username": "customer-mybrand-cc-us",
        "password": "SecretProxyPassword123"
    }
)

Price Volatility Analytics: Calculating Moving Averages & Trend Deltas

Naive price monitoring bots trigger false-positive alerts every time a competitor adjusts pricing by a few pennies or tests micro-conversions. An intelligent price sentinel calculates historical moving averages and statistical volatility to detect true promotional events.

SQL Analytics: 7-Day Exponential Moving Average (EMA)

SELECT 
  product_id,
  price as latest_price,
  round(avg(price) OVER (
    PARTITION BY product_id 
    ORDER BY checked_at 
    ROWS BETWEEN 14 PRECEDING AND CURRENT ROW
  ), 2) as rolling_7day_avg,
  round(((price - avg(price) OVER (
    PARTITION BY product_id 
    ORDER BY checked_at 
    ROWS BETWEEN 14 PRECEDING AND CURRENT ROW
  )) / avg(price) OVER (
    PARTITION BY product_id 
    ORDER BY checked_at 
    ROWS BETWEEN 14 PRECEDING AND CURRENT ROW
  )) * 100, 2) as deviation_from_baseline_pct
FROM price_logs
WHERE product_id = 42
ORDER BY checked_at DESC
LIMIT 1;

If the current observed price deviates by more than -10% from the 7-day rolling average, the bot classifies the event as an aggressive promotional push or clearance sale, elevating the alert priority in your Telegram channel.

Interactive Telegram Alerts: Inline Buttons & Automated Repricing

The Telegram Bot API supports Inline Keyboards (clickable buttons beneath chat messages). Instead of merely reading an alert, your pricing manager can take immediate strategic action directly from the Telegram notification:

Telegram Inline Keyboard Payload (Python)

reply_markup = {
    "inline_keyboard": [
        [
            {"text": "๐Ÿ›’ Open Competitor Listing", "url": competitor_url},
            {"text": "โšก Match Price in Shopify", "callback_data": f"match_price_{product_sku}_{new_price}"}
        ],
        [
            {"text": "๐Ÿ“‰ Undercut by 5%", "callback_data": f"undercut_{product_sku}_{round(new_price * 0.95, 2)}"},
            {"text": "๐Ÿ”• Mute Alerts (24h)", "callback_data": f"mute_{product_sku}"}
        ]
    ]
}

When the user taps "Match Price in Shopify", the Telegram webhook dispatches a callback query to your microservice, which calls the Shopify Admin REST API to update your store’s variant price in millisecondsโ€”turning competitive intelligence into autonomous revenue protection.

High-Concurrency Worker Architecture: Python asyncio.Queue

When tracking thousands of e-commerce product URLs, sequential scraping (visiting URL 1, waiting 3 seconds, visiting URL 2) is too slow to catch flash sales. A high-throughput price sentinel implements an asynchronous producer-consumer worker pool using Python’s asyncio.Queue.

Producer-Consumer Playwright Architecture

  • The Producer: Reads pending URLs from SQLite or Redis and feeds task payloads into an internal asyncio.Queue() buffer.
  • Isolated Browser Contexts: A single master Chromium instance launches 10 to 15 lightweight browser.new_context() workers. Each context maintains isolated cookies, cache, and proxy bindings with minimal RAM overhead compared to launching multiple distinct browser processes.
  • Adaptive Concurrency Throttling: Workers monitor HTTP response status codes. If a domain returns HTTP 429 (Too Many Requests), the worker temporarily backs off for that specific domain queue while other workers continue processing unrelated competitor websites uninterrupted.

Production Async Worker Pool Pattern (Code Snippet)

async def price_worker(worker_id, queue, browser, db_conn):
    context = await browser.new_context()
    page = await context.new_page()
    while True:
        product = await queue.get()
        try:
            price, in_stock = await scrape_product(page, product["url"])
            await record_price(db_conn, product["id"], price, in_stock)
        finally:
            queue.task_done()

Monitoring Inventory Stockouts & Variant Availability

Pricing intelligence is only half the competitive battle; inventory availability is equally profitable. When a primary competitor goes out of stock on their best-selling SKU, their customers actively search for alternatives. Detecting competitor stockouts allows your marketing team to immediately increase Google Search ad bids on competitor brand keywords.

Stockout Extraction Heuristics

  1. JSON-LD Availability Schema: Modern stores populate offers.availability with standardized Schema.org URLs: http://schema.org/InStock vs http://schema.org/OutOfStock or http://schema.org/PreOrder.
  2. Add-to-Cart Button Inspection: Check the disabled state of the purchase action element: button[disabled], [aria-disabled="true"], or inner text matching "Sold Out" or "Out of Stock".
  3. High-Impact Telegram Alert: When a competitor’s flagship product shifts from InStock to OutOfStock, the bot dispatches an immediate high-priority alert: "๐Ÿšจ OPPORTUNITY ALERT: Competitor Apex has stocked out of SKU #104. Consider elevating ad spend on competing catalog keywords!"

Handling Cloudflare Turnstile Interstitial Challenges

A frequent challenge when scraping enterprise storefronts is encountering Cloudflare Turnstile challenges ("Checking if the site connection is secure"). Standard headless Chromium instances stall indefinitely on the challenge iframe. To overcome Turnstile automatically without expensive human CAPTCHA-solving farms:

Automated Turnstile Bypass Architecture

  • Patchright Runtime: Replace raw Playwright with Patchright, an undetectable drop-in Playwright fork that modifies native C++ Chromium binary source code, eliminating internal DevTools protocol fingerprint artifacts.
  • Realistic User Profile Persistence: Store persistent browser user data directories (user_data_dir) on disk. When a context passes a challenge once, Cloudflare sets clearance cookies (cf_clearance) that persist across subsequent runs for up to 30 days.
  • Interactive Frame Resolution: Use automated cursor movements to click inside the Turnstile challenge checkbox iframe if an interactive checkbox is presented.

Summary Checklist: Launching Your Autonomous Price Sentinel

Before launching your price monitoring bot into full production, verify this operational checklist:

  1. Database Migration: Verify that your SQLite database (competitor_prices.db) tables and indexes are properly initialized.
  2. Telegram Bot Handshake: Send a test message via your bot token to ensure push notifications trigger your smartphone lock screen.
  3. Anti-Detection Verification: Run a test crawl against https://bot.sannysoft.com to verify that all bot detection parameters return clean green passes.
  4. Daemon Health Monitoring: Configure systemd or Docker restart policies (restart: always) to ensure the bot recovers automatically from host reboots or network interruptions.

Frequently Asked Questions: Competitor Price Tracking Bots

Is it legal to scrape competitor prices online?

In the United States, landmark legal precedents (such as hiQ Labs v. LinkedIn) have established that scraping publicly accessible internet data that is not protected by an authentication login does not violate the Computer Fraud and Abuse Act (CFAA). However, always ensure your bot does not violate copyright, trademark, or DDoS laws.

Why use Telegram for alerts instead of Discord or Slack?

Telegram provides an exceptionally fast mobile push notification engine, lightweight bot API creation via @BotFather in under 60 seconds, zero monthly fees, and completely reliable message delivery directly to your smartphone lock screen.

How do I create a Telegram Bot and find my Chat ID?

Message @BotFather on Telegram and send /newbot to receive your API Token. Then message @userinfobot or query https://api.telegram.org/bot<TOKEN>/getUpdates after sending your bot a message to retrieve your numerical Chat ID.

Can this bot track Amazon or Walmart prices?

Yes, but Amazon and Walmart utilize sophisticated anti-bot systems requiring rotating residential proxies and Playwright stealth plugins. For Amazon specifically, tracking price changes can also be supplemented using Amazon’s official Creators/PA-API.

How many product URLs can a single $5 VPS track?

A standard 1 vCPU, 2GB RAM server running Playwright sequentially can easily monitor 500 to 1,500 product URLs on a 1-hour or 2-hour rotation without exceeding server resources.

MH

Written by Mamun Hossain

Founder & Systems Architect

Mamun is a performance marketing consultant, systems architect, and founder of Zelobiz with over 8 years of experience scaling paid acquisition, server-side attribution, and workflow automations. He independently tests and audits enterprise SaaS software, developer utilities, and ergonomic workspace gear.

Leave a Comment