Price Tracking

Building Real-time Amazon Price Monitoring with Scraping API

Step-by-step guide to creating an automated Amazon price tracking system using Pangol Info's e-commerce API

December 12, 2025 22 min read Pangol Info Team

In the fast-paced ecosystem of Amazon e-commerce, prices fluctuate millions of times a day. For serious sellers, the 'set it and forget it' pricing strategy is a one-way ticket to obscurity. To stay competitive, maintain the Buy Box, and protect your profit margins, you need a robust, real-time price monitoring system. This guide provides a complete technical walkthrough for building your own automated price tracker from scratch, using Python and the Pangolin Scraping API.

Unlike off-the-shelf tools that can be expensive and inflexible, a custom solution gives you complete control over data granularity, update frequency, and integration with your backend systems. Whether you are a private label seller protecting your brand value or a reseller fighting for the Buy Box, the system we build today will become the backbone of your pricing intelligence operations.

Why Real-time Price Monitoring is Non-Negotiable

Dynamic pricing is the heartbeat of Amazon. The days of static price tags are long gone, replaced by sophisticated algorithms that adjust prices based on demand, competition, and inventory levels. If you aren't monitoring these changes in real-time, you're flying blind.

The Buy Box Battle: The Amazon Buy Box (the "Add to Cart" button) drives over 82% of all sales. Amazon's algorithm awards the Buy Box based heavily on price competitiveness. A difference of just a few cents can win or lose you the sale. Automated monitoring ensures you know exactly when you lose the Buy Box and why, allowing for immediate corrective action.

Competitor Strategy Decoding: Your competitors are testing strategies constantly. Are they lowering prices at night? Are they running flash sales on weekends? By collecting continuous price data points, you can reverse-engineer their strategies. For instance, you might discover that a major competitor systematically drops prices by 5% every Tuesday—knowledge that allows you to preemptively adjust your strategy.

Profit Maximization vs. Revenue: It's not always about the lowest price. Sometimes, competitors run out of stock or raise their prices. In these moments, a smart system detects the opportunity to raise your prices while still maintaining the Buy Box, significantly boosting your profit margins. We've seen sellers increase their net profit by 15-20% simply by capturing these "high-price" windows that manual checking would miss.

Data Insight

According to recent market analysis, sellers utilizing automated repricing strategies based on real-time data see an average sales increase of 42% within the first three months compared to those using manual or static pricing.

Price Monitoring Fundamentals

Before writing code, we must understand the data points that constitute a "price" on Amazon. It isn't just a single number. To build an effective tracker, you need to capture the following attributes for every snapshot:

  • Current Buy Box Price: The price customers pay when they click "Buy Now". This is your most critical metric.
  • List Price: The strikethrough price, important for calculating discounts.
  • Shipping Cost: Often hidden, but part of the total landing price.
  • Seller ID: Who is winning the Buy Box? Is it Amazon (Sold by Amazon) or a 3rd party?
  • Availability: Is the product in stock? Back-ordered?
  • Offer Count: How many other sellers are on this listing?

Tracking Strategy & Frequency

How often should you check prices? Over-checking wastes API credits; under-checking misses critical movements.

High-Velocity Products

Check every 15-30 minutes. These items (top 1% BSR) have aggressive repricers running constantly.

Long-Tail Products

Check every 6-12 hours. Prices are stickier and change less frequently.

Building the Core Tracking System

Now, let's build the engine. We will use Python with the requests library to interact with Pangol Info's Scrape API, and sqlite3 for a lightweight, serverless database to store our price history.

The PriceTracker class below handles three main functions:

  1. Database Initialization: Sets up a table to store price history.
  2. Data Scraping: Sends a request to the API to get the latest product data.
  3. Data Persistence: Saves the parsed data into our database.

import requests
import sqlite3
import json
from datetime import datetime
from typing import Dict, Optional

class PriceTracker:
    """
    Core engine for tracking Amazon product prices via Pangol Info API.
    Handles data extraction and persistence to SQLite.
    """
    
    def __init__(self, api_key: str, db_path: str = "price_history.db"):
        self.api_key = api_key
        self.api_endpoint = "https://scrapeapi.pangolinfo.com/api/v1/scrape"
        self.db_path = db_path
        self._init_db()

    def _init_db(self):
        """Initialize the SQLite database schema."""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS prices (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                asin TEXT NOT NULL,
                timestamp DATETIME NOT NULL,
                price REAL,
                currency TEXT,
                seller_name TEXT,
                is_amazon_sold BOOLEAN,
                availability TEXT,
                rating REAL,
                reviews_count INTEGER
            )
        ''')
        # Index for faster queries later
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_asin ON prices(asin)')
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_time ON prices(timestamp)')
        conn.commit()
        conn.close()

    def get_product_data(self, asin: str) -> Optional[Dict]:
        """
        Fetch real-time product data from Amazon via API.
        
        Args:
            asin: Amazon Standard Identification Number
            
        Returns:
            Dictionary containing parsed product details or None if failed.
        """
        payload = {
            "url": f"https://www.amazon.com/dp/{asin}",
            "parserName": "amzDetail",  # Use dedicated detail page parser
            "format": "json",
            "bizContext": {
                "zipcode": "10001"  # Check prices for NY delivery
            }
        }
        
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }

        try:
            response = requests.post(self.api_endpoint, json=payload, headers=headers)
            
            if response.status_code == 200:
                result = response.json()
                # Navigate through the API response structure
                if result.get("code") == 0 and result.get("data"):
                    return result["data"]["json"][0]["data"]
            
            print(f"Failed to fetch data for {asin}: {response.text}")
            return None
            
        except Exception as e:
            print(f"Error requesting {asin}: {str(e)}")
            return None

    def store_price_point(self, asin: str, data: Dict):
        """Save a single price snapshot to the database."""
        if not data:
            return

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # safely extract numerical price
        raw_price = data.get("price", 0)
        
        cursor.execute('''
            INSERT INTO prices (
                asin, timestamp, price, currency, seller_name, 
                is_amazon_sold, availability, rating, reviews_count
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            asin,
            datetime.now().isoformat(),
            raw_price,
            data.get("currency", "USD"),
            data.get("seller_name", "Unknown"),
            data.get("seller_name", "").lower() == "amazon.com",
            data.get("availability", "Unknown"),
            data.get("star", 0.0),
            data.get("rating_count", 0)
        ))
        
        conn.commit()
        conn.close()
        print(f"✅ Stored price for {asin}: ${raw_price}")

    def run_check(self, asins: list):
        """Batch process a list of ASINs."""
        print(f"Starting price check for {len(asins)} products...")
        for asin in asins:
            data = self.get_product_data(asin)
            if data:
                self.store_price_point(asin, data)
        print("Batch check complete.")

# Usage Example
if __name__ == "__main__":
    tracker = PriceTracker(api_key="YOUR_API_KEY_HERE")
    my_products = ["B08N5WRWNW", "B09G9F5T3R"] # Example ASINs
    tracker.run_check(my_products)

This PriceTracker is robust because it handles the variability of web data. It stores not just the price, but context—who is selling (Sold by Amazon?) and availability. If a competitor's price drops to $0.01 but they are "Currently Unavailable", your system shouldn't panic. Context is key.

Advanced Price Analysis

Raw data is useless without interpretation. Is a $20 price good or bad? It depends on the history. The PriceAnalyzer class queries our SQLite database to find trends, volatility, and competitive positioning.

import pandas as pd
import sqlite3
import numpy as np

class PriceAnalyzer:
    """
    Analyzes historical price data to find trends and compute statistics.
    Uses pandas for efficient data manipulation.
    """
    
    def __init__(self, db_path: str = "price_history.db"):
        self.db_path = db_path

    def get_price_history_df(self, asin: str, days: int = 30) -> pd.DataFrame:
        """Load price history for an ASIN into a pandas DataFrame."""
        conn = sqlite3.connect(self.db_path)
        query = f"""
            SELECT timestamp, price, seller_name, is_amazon_sold 
            FROM prices 
            WHERE asin = '{asin}' 
            AND timestamp >= datetime('now', '-{days} days')
            ORDER BY timestamp ASC
        """
        df = pd.read_sql_query(query, conn)
        conn.close()
        
        if not df.empty:
            df['timestamp'] = pd.to_datetime(df['timestamp'])
            df.set_index('timestamp', inplace=True)
        return df

    def analyze_volatility(self, asin: str) -> dict:
        """
        Calculate price volatility metrics.
        High std_dev means the price fluctuates frequently.
        """
        df = self.get_price_history_df(asin)
        if df.empty:
            return {}

        current_price = df['price'].iloc[-1]
        mean_price = df['price'].mean()
        min_price = df['price'].min()
        max_price = df['price'].max()
        std_dev = df['price'].std()
        
        # Calculate price momentum (simple moving average crossover)
        short_ma = df['price'].rolling(window=3).mean().iloc[-1]
        long_ma = df['price'].rolling(window=10).mean().iloc[-1]
        trend = "UP" if short_ma > long_ma else "DOWN"

        return {
            "asin": asin,
            "current_price": current_price,
            "mean_price": round(mean_price, 2),
            "volatility_index": round(std_dev, 2),
            "price_range": (min_price, max_price),
            "trend": trend,
            "is_amazon_dominating": self._check_amazon_dominance(df)
        }

    def _check_amazon_dominance(self, df: pd.DataFrame) -> bool:
        """Check if Amazon accounts for >50% of the Buy Box history."""
        amazon_wins = df[df['is_amazon_sold'] == 1].shape[0]
        total_checks = df.shape[0]
        return (amazon_wins / total_checks) > 0.5 if total_checks > 0 else False

    def compare_competitors(self, my_asin: str, competitor_asins: list) -> dict:
        """Compare my product price against a basket of competitors."""
        my_stats = self.analyze_volatility(my_asin)
        if not my_stats:
            return {"error": "No data for my ASIN"}

        my_price = my_stats['current_price']
        competitor_prices = []

        for comp_asin in competitor_asins:
            comp_stats = self.analyze_volatility(comp_asin)
            if comp_stats:
                competitor_prices.append(comp_stats['current_price'])

        if not competitor_prices:
            return {"position": "UNKNOWN"}

        avg_market_price = np.mean(competitor_prices)
        
        position = "PREMIUM"
        if my_price < avg_market_price * 0.95:
            position = "VALUE"
        elif my_price <= avg_market_price * 1.05:
            position = "COMPETITIVE"

        return {
            "my_price": my_price,
            "market_average": round(avg_market_price, 2),
            "price_index": round((my_price / avg_market_price) * 100, 1),
            "market_position": position
        }

Using compare_competitors, you can programmatically define your market position. Are you a luxury brand ("PREMIUM") or a budget choice ("VALUE")? If you are priced like a luxury brand but your reviews say "cheap quality", you have a problem. This data aligns your pricing with reality.

Automation and Alerts

You can't stare at a dashboard all day. We need an AlertSystem that pushes critical information to you via Email or Slack. We will implement a system that triggers when:

  • Buy Box Loss: You are no longer the winner.
  • Price War: A competitor undercuts you by more than 10%.
  • Amazon Entry: Amazon suddenly starts selling on the listing (a huge red flag).

import smtplib
from email.mime.text import MIMEText
import requests

class AlertSystem:
    """
    Sends notifications to external channels based on price events.
    Supports Email (SMTP) and Slack Webhooks.
    """
    
    def __init__(self, smtp_config: dict, slack_webhook: str = None):
        self.smtp_config = smtp_config
        self.slack_webhook = slack_webhook

    def send_email(self, subject: str, message: str):
        """Send an email alert."""
        msg = MIMEText(message)
        msg['Subject'] = f"[Price Alert] {subject}"
        msg['From'] = self.smtp_config['sender']
        msg['To'] = self.smtp_config['recipient']

        try:
            with smtplib.SMTP_SSL(self.smtp_config['host'], 465) as server:
                server.login(self.smtp_config['user'], self.smtp_config['password'])
                server.send_message(msg)
            print("✉️ Email alert sent.")
        except Exception as e:
            print(f"Failed to send email: {e}")

    def send_slack(self, message: str):
        """Send a Slack notification."""
        if not self.slack_webhook:
            return
        
        payload = {"text": f"🚨 {message}"}
        try:
            requests.post(self.slack_webhook, json=payload)
            print("💬 Slack alert sent.")
        except Exception as e:
            print(f"Failed to send to Slack: {e}")

    def check_conditions(self, asin: str, previous_data: dict, current_data: dict):
        """
        Evaluate business rules and trigger alerts.
        """
        # Rule 1: Buy Box Loss
        if previous_data.get("seller_name") == "MyStore" and current_data.get("seller_name") != "MyStore":
            self.send_slack(f"⚠️ Buy Box LOST for {asin}! Winner: {current_data.get('seller_name')} @ ${current_data.get('price')}")

        # Rule 2: Significant Price Drop (>10%)
        prev_price = previous_data.get("price", 0)
        curr_price = current_data.get("price", 0)
        
        if prev_price > 0 and curr_price < prev_price * 0.9:
            drop_pct = round((1 - curr_price/prev_price) * 100, 1)
            self.send_email(
                subject=f"Price Crash Alert: {asin}",
                message=f"Price dropped by {drop_pct}% from ${prev_price} to ${curr_price}. Check competitor activity immediately."
            )

        # Rule 3: Amazon entering the listing
        if not previous_data.get("is_amazon_sold") and current_data.get("is_amazon_sold"):
            self.send_slack(f"😱 DANGER: Amazon is now selling on {asin}. Expect aggressive pricing.")

Dynamic Pricing Strategies

Monitoring is passive; repricing is active. With accurate data, you can implement dynamic pricing strategies similar to what airlines use. Be careful: automated repricing carries risk. You must always have a "floor price" (minimum acceptable price) to prevent selling at a loss.

class DynamicPricer:
    """
    Calculates optimal pricing based on competitive landscape and business rules.
    does NOT automatically update Amazon (safety first), but suggests changes.
    """

    def __init__(self, floor_margin: float = 0.15):
        self.floor_margin = floor_margin  # Minimum 15% profit margin

    def get_optimal_price(self, my_cost: float, competitor_price: float, strategy: str = "MATCH") -> float:
        """
        Determine the best price.
        
        Strategies:
        - MATCH: Match the Buy Box price.
        - UNDERCUT: Pency-drop under the competitor.
        - PREMIUM: Stay 10% above (for brand perception).
        """
        floor_price = my_cost * (1 + self.floor_margin)
        suggested_price = floor_price

        if strategy == "MATCH":
            suggested_price = competitor_price
        
        elif strategy == "UNDERCUT":
            suggested_price = competitor_price - 0.01
        
        elif strategy == "PREMIUM":
            suggested_price = competitor_price * 1.10

        # SAFETY CHECK: Never go below floor price
        if suggested_price < floor_price:
            print(f"⚠️ Strategy {strategy} would breach floor price ${floor_price}. Capping at floor.")
            return floor_price
            
        return round(suggested_price, 2)

    def simulate_repricing(self, products: list):
        """Run a simulation to see how strategies would perform."""
        print("--- Repricing Simulation ---")
        for p in products:
            new_price = self.get_optimal_price(p['cost'], p['competitor_price'], p['strategy'])
            print(f"Product {p['asin']}: Current ${p['competitor_price']} -> Suggest ${new_price} ({p['strategy']})")

Strategy Breakdown

1. The Penny Dropper (Undercut): Good for commodity items where price is the only differentiator. Be wary of "Race to the Bottom" wars.

2. The Premium Anchor (Premium): If you have better reviews (4.5 vs 4.0 stars), you should charge more. Customers pay for quality. Use the API to check your review count relative to competitors before applying this.

3. The Inventory Cleaner: If availability < 5 units, increase price to slow down sales and prevent stock-out (which hurts organic ranking). If inventory> 1000, switch to aggressive undercutting to liquidate.

Real-world Implementation

To take this from a script to a production system, you need infrastructure.

  • Cloud Hosting: Run your PriceTracker cron job on an AWS EC2 micro instance or a $5/month DigitalOcean droplet. Do not run it on your laptop.
  • Proxies & IP Rotation: Amazon is aggressive about blocking scrapers. This is why we use Pangolin Scrape API. It handles IP rotation, headers, and CAPTCHAs automatically, ensuring 99.9% success rate without you managing a proxy pool.
  • Database: SQLite is great for <1GB of data. If you track 10,000+ products, migrate to PostgreSQL or Amazon RDS.

30-Day Implementation Plan

W1

Week 1: Data Accumulation

Do not reprice yet. Run the tracker silently to build a baseline of price history. Understand the volatility.

W2

Week 2: Analysis & Segmentation

Use PriceAnalyzer to segment products into "Volatile" vs "Stable". Identify your main competitors.

W3

Week 3: Alert Customization

Tune your AlertSystem thresholds. Reduce noise. Only get alerted for actionable events.

W4

Week 4: Automated Repricing

Turn on the DynamicPricer logic for a small batch involving 5-10 products. Monitor closely.

Conclusion

Price is the single most powerful lever you have in e-commerce. By building this automated monitoring system, you transform pricing from a guessing game into a data science. You stop leaving money on the table.

Remember, the goal isn't just to react, but to predict. With enough history stored in your database, you can start forecasting competitor moves before they happen. Start small, track your best-sellers, and scale your intelligence as you grow.

Ready to Build Your Tracker?

Get your free API key today and start monitoring up to 1,000 requests for free. No credit card required.

Get Free API Key