Skip to main content
Use Cases/Use Case / Price Monitoring

Web Scraping for Price Monitoring

Use fastCRW to scrape competitor prices, track e-commerce changes, and trigger alerts when prices shift across markets.

Published
May 12, 2026
Updated
May 12, 2026
Category
use cases
Verdict

fastCRW is ideal for continuous price tracking at scale. Render JavaScript for dynamic pricing, extract structured data via JSON schemas, and build alerts on top of clean, timestamped data.

Scrape e-commerce sites with JavaScript rendering for dynamic pricingExtract structured prices into JSON using Pydantic-like schemasDetect price changes via automated diffing and trigger alerts

Verdict

Price monitoring is one of the highest-ROI scraping use cases. Retailers adjust prices daily. Marketplaces run dynamic promotions. Missing a price drop or competitor move can cost thousands in lost sales or margin. fastCRW solves this by making it cheap and easy to track prices at scale: render JavaScript to capture dynamic pricing, extract structured data into comparable fields, and build alerts on top of timestamped snapshots.

Why This Matters

Price is the most volatile and value-sensitive signal in e-commerce. Manual price tracking does not scale past 10–20 products. By the time an analyst checks a competitor's site, the price may have changed three times.

Automated price monitoring gives you:

  • continuous tracking without analyst overhead,
  • real-time alerts when competitors move,
  • historical data to detect seasonal and trend-based pricing patterns,
  • and the ability to optimize your own prices based on competitor signals.

Real-world impact:

  • Retailers adjust prices upward in slow sales periods and downward when inventory builds. Missing a 20% drop can cost you a sale.
  • Marketplaces (Amazon, eBay) change prices hourly based on demand. Price intelligence informs repricing strategy.
  • SaaS and subscriptions often discount during promotional windows. Tracking competitor discounts keeps you competitive.
  • Travel and hospitality prices fluctuate by day and occupancy. Real-time alerts tell you when to undercut or hold firm.

Where fastCRW Helps

Monitoring needfastCRW role
Competitor pricing pagesscrape with Chrome rendering to extract dynamic prices
Marketplace listingsscrape Amazon, eBay, Shopify product pages with JSON extraction
Price change detectionStore snapshots, diff against baseline, trigger alerts
Multi-source aggregationcrawl a site's category pages, scrape each product with structured extraction
Historical trend analysisBuild time-series database of prices, detect seasonality and promotions

Typical Flow

  1. Start with a list of competitor product URLs or category pages.
  2. For each URL, scrape with Chrome rendering and structured extraction (JSON schema).
  3. Store the result (price, availability, currency, timestamp) in a database.
  4. On the next scheduled scrape (daily, hourly, or per-minute for high-velocity markets), compare new prices against the baseline.
  5. Detect changes: price drop, price increase, out-of-stock, new competitor, promotion ended.
  6. Trigger alerts via email, Slack, SMS, or webhook.
  7. Log all changes to a time-series database for trend analysis and historical replay.

Good Fits

  • E-commerce teams tracking competitor prices and adjusting their own pricing engine,
  • Marketplace sellers (Amazon, eBay, Shopify) monitoring their category and pricing rank,
  • Price comparison sites (PriceGrabber, Honey, CamelCamelCamel) aggregating prices across retailers,
  • Dynamic pricing engines feeding real-time competitor data into ML pricing models,
  • and brand teams protecting MAP (Minimum Advertised Price) by monitoring unauthorized discounting.

Architecture

┌─────────────────────────────────────────────────────────────┐
│ Price Monitoring Pipeline with fastCRW                      │
└─────────────────────────────────────────────────────────────┘

[Competitor URLs] ──→ [fastCRW Scrape] ──→ [Extract JSON]
                      (Chrome render)      (Pydantic schema)
                                                  │
                                                  ↓
[Time-Series DB] ←── [Diff & Compare] ←── [Store + Timestamp]
                      (Detect changes)
                                                  │
                                                  ↓
[Alerts & Notifications] ←── [Alert Logic]
(Email, Slack, Webhook)   (Price drop, discount, OOS)
                                                  │
                                                  ↓
[Dashboard] ←── [Historical Analysis]
(Trends, seasonality)

Key Components

Scrape layer: Use fastCRW's Chrome rendering (js_enabled: true) to render dynamic pricing, countdown timers, flash sales, and real-time stock status.

Extraction layer: Define a reusable JSON schema (Pydantic-style) for price fields. Include product ID, name, original price, sale price, currency, discount percentage, availability, and timestamp.

Storage layer: PostgreSQL or SQLite with a price_snapshots table (product_id, url, price, timestamp). Query by product_id to get historical price series.

Diff layer: On each new scrape, load the previous snapshot and compare fields. Flag which fields changed.

Alert layer: Rules engine that triggers notifications based on change thresholds (price dropped >10%, price increased while inventory high, competitor entered market, price below threshold).

Implementation Walkthrough

Here's a working example of a daily price monitor for e-commerce products. This code:

  1. Scrapes a list of competitor product URLs
  2. Extracts prices using structured JSON extraction
  3. Stores results with timestamps
  4. Detects price changes
  5. Triggers alerts (email placeholder)
import requests
import json
from datetime import datetime
import sqlite3
from typing import Optional

# Initialize database
conn = sqlite3.connect("prices.db")
cursor = conn.cursor()
cursor.execute("""
    CREATE TABLE IF NOT EXISTS price_snapshots (
        id INTEGER PRIMARY KEY,
        product_id TEXT,
        product_name TEXT,
        url TEXT,
        price REAL,
        original_price REAL,
        currency TEXT,
        in_stock BOOLEAN,
        discount_pct REAL,
        scraped_at TIMESTAMP
    )
""")
conn.commit()

# Define extraction schema for pricing
PRICE_SCHEMA = {
    "type": "object",
    "properties": {
        "product_name": {"type": "string"},
        "product_id": {"type": "string"},
        "current_price": {"type": "number"},
        "original_price": {"type": "number"},
        "discount_percentage": {"type": "number"},
        "currency": {"type": "string"},
        "in_stock": {"type": "boolean"}
    },
    "required": ["product_name", "current_price", "currency"]
}

# Function to scrape a product page
def scrape_product_price(url: str) -> Optional[dict]:
    """Scrape a product page and extract price data."""
    api_key = "your_fastcrw_api_key"
    
    response = requests.post(
        "https://api.fastcrw.com/v1/scrape",
        headers={"Authorization": f"Bearer {api_key}"},
        json={
            "url": url,
            "js_enabled": True,  # Enable Chrome rendering for dynamic content
            "formats": ["json"],
            "extraction": {
                "schema": PRICE_SCHEMA
            }
        }
    )
    
    if response.status_code == 200:
        data = response.json()
        if data.get("success"):
            return data.get("data", {})
    
    print(f"Failed to scrape {url}: {response.status_code}")
    return None

# Function to detect price changes
def detect_price_change(product_id: str, new_price: float) -> Optional[dict]:
    """Compare new price against the most recent baseline."""
    cursor.execute("""
        SELECT price, scraped_at FROM price_snapshots
        WHERE product_id = ?
        ORDER BY scraped_at DESC
        LIMIT 1
    """, (product_id,))
    
    result = cursor.fetchone()
    if not result:
        return None  # No baseline yet
    
    old_price, old_time = result
    change_pct = ((new_price - old_price) / old_price * 100) if old_price > 0 else 0
    
    if abs(change_pct) > 0.5:  # Alert if price changed by >0.5%
        return {
            "old_price": old_price,
            "new_price": new_price,
            "change_pct": round(change_pct, 2),
            "previous_scraped_at": old_time
        }
    
    return None

# Function to store and alert
def process_product(url: str):
    """Scrape, store, detect changes, and alert."""
    extracted = scrape_product_price(url)
    
    if not extracted:
        print(f"Skipping {url}: extraction failed")
        return
    
    # Extract fields from the structured JSON
    product_id = extracted.get("product_id", "unknown")
    product_name = extracted.get("product_name", "Unknown")
    current_price = extracted.get("current_price")
    original_price = extracted.get("original_price")
    currency = extracted.get("currency", "USD")
    in_stock = extracted.get("in_stock", True)
    discount_pct = extracted.get("discount_percentage", 0)
    
    # Store the snapshot
    cursor.execute("""
        INSERT INTO price_snapshots
        (product_id, product_name, url, price, original_price, currency, in_stock, discount_pct, scraped_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    """, (product_id, product_name, url, current_price, original_price, currency, in_stock, discount_pct, datetime.now()))
    conn.commit()
    
    # Detect change
    change = detect_price_change(product_id, current_price)
    
    if change:
        print(f"ALERT: {product_name} ({product_id}) changed by {change['change_pct']}%")
        print(f"  Old: {currency} {change['old_price']:.2f} → New: {currency} {current_price:.2f}")
        
        # Trigger notification (email, Slack, webhook, etc.)
        send_alert(product_name, change, currency)
    else:
        print(f"OK: {product_name} ({product_id}) - {currency} {current_price:.2f}")

# Placeholder alert function
def send_alert(product_name: str, change: dict, currency: str):
    """Send alert via email, Slack, or webhook."""
    # TODO: Implement email/Slack/webhook notification
    print(f"  → Sending alert for {product_name}")

# Main loop
if __name__ == "__main__":
    competitor_urls = [
        "https://example.com/product/widget-a",
        "https://competitor.com/product/gadget-b",
        "https://amazon.com/dp/B000000001",
    ]
    
    for url in competitor_urls:
        process_product(url)
    
    conn.close()

How it works:

  1. Scrape: POST /v1/scrape with js_enabled: True and a JSON schema for extraction.
  2. Extract: fastCRW parses the rendered HTML and extracts structured data into your schema.
  3. Store: Save the extracted fields with a timestamp to the database.
  4. Diff: Query the previous baseline and calculate percentage change.
  5. Alert: If change exceeds threshold (0.5%), send a notification.

Cost estimate: 100 products × 30 days = 3,000 scrapes. Pro tier (~$0.005/page) = $15/month. Business tier with residential proxy ($0.01/page) = ~$30/month.

Production Considerations

Rate Limiting and Respect

E-commerce sites are vigilant about bot traffic. To avoid blocks:

  • Space requests: 30–60 seconds between requests to the same domain.
  • Rotate User-Agents: Use realistic browser User-Agent strings.
  • Respect robots.txt: Check /robots.txt before adding new URLs.
  • Use Residential Proxies: For high-frequency scraping (>10 requests/hour per domain), use residential proxy rotation (Business tier).
  • Monitor HTTP status codes: 429 (rate limited), 403 (forbidden), 503 (temporarily unavailable) indicate you need to slow down.

Handling Anti-Bot Protection

Amazon, Shopify, and other platforms use anti-bot measures:

  • CAPTCHA: fastCRW cannot solve CAPTCHAs. If a page behind CAPTCHA becomes your primary data source, consider their official API.
  • Cloudflare: fastCRW's Chrome rendering handles most Cloudflare challenges automatically.
  • Fingerprinting: Cloudflare and others detect browser automation. Chrome rendering mimics a real browser, but high request volume will still trigger blocks. Use residential proxies and slow your request rate.
  • IP rotation: Residential proxies rotate IPs per request. For high-velocity scraping, enable per-domain IP rotation.

JavaScript Rendering Costs

  • LightPanda (Pro tier): ~$0.001 per page. Fast, light, supports basic JS.
  • Chrome (Business tier): ~$0.005 per page. Full browser automation, handles complex JS.

For 100 daily scrapes:

  • HTTP only: 100 × 30 = 3,000 credits @ ~$3 = ~$9/month.
  • LightPanda JS: 3,000 credits @ ~$4 = ~$12/month.
  • Chrome JS: 3,000 credits @ ~$8 = ~$24/month.

Choose based on how many competitor sites require JS rendering.

Handling Out-of-Stock and Discontinued Products

Real e-commerce has dynamics:

  • Out-of-stock: Flag in your schema. Don't alert on missing prices; alert on transitions (in-stock → out-of-stock).
  • Discontinued products: Track URL 404s. Remove from monitoring list or set TTL on old products.
  • Regional availability: Some products are geo-locked. If a price disappears, check if it's a geo issue or discontinuation.

Scaling to Thousands of Products

For 10K+ products:

  • Batch scraping: Use /v1/batch/scrape to scrape multiple URLs in a single request (faster than sequential scrapes).
  • Distributed workers: Run scraping on multiple workers (each with its own IP/proxy) to parallelize across competitors.
  • Time-series database: PostgreSQL with time-series extensions (TimescaleDB) scales better than SQLite for millions of price points.
  • Alert aggregation: Don't send one email per price change. Batch alerts into a daily digest or use Slack threads.

Pricing Math

ScenarioScrapes/monthRate/pageTotal cost
50 products, daily, HTTP1,500$0.003~$4.50
100 products, daily, HTTP3,000$0.003~$9.00
100 products, daily, JS (Light)3,000$0.004~$12.00
100 products, daily, JS (Chrome)3,000$0.008~$24.00
1,000 products, daily, HTTP + Proxy30,000$0.008~$240.00

Pro tip: Start with HTTP for static pages (pricing pages with hardcoded HTML). Upgrade to Chrome rendering only for pages that truly need JavaScript (React-based storefronts, dynamic flash sales).

FAQ

Q: How do I handle multiple currencies?
A: Include currency as a field in your extraction schema. Store alongside the price. For cross-currency comparison, use a real-time FX API (OANDA, Alpha Vantage) to convert to a base currency.

Q: Can I track inventory levels too?
A: Yes. Add stock_quantity or in_stock to your schema. Track inventory as time-series data. This helps you predict price drops (when inventory is high, retailers drop prices to clear stock).

Q: How do I handle duplicate products across sellers?
A: Add a normalized product identifier (SKU, GTIN, ASIN) to your schema. Map all URLs and sellers back to the canonical product. This lets you compare prices across multiple retailers for the same product.

Q: What if a competitor blocks my IP?
A: fastCRW's Business tier includes residential proxy pools. Each request routes through a different residential IP. If you hit strict rate limits, slow down (increase time between requests) or switch to a different proxy provider via fastCRW's proxy configuration.

Q: Should I monitor prices every hour or every day?
A: Depends on market velocity. Daily works for most retail. Hourly (or per-minute) for flash sales, travel pricing, or auction-based marketplaces. Each extra scrape costs money; balance freshness against budget.

Q: How do I export price history for analysis?
A: Query your database by date range. Export to CSV for analysis in Pandas, Excel, or Tableau. Time-series databases (TimescaleDB, ClickHouse) make trend analysis easier than SQLite for large datasets.

  • Firecrawl alternatives — managed scraping API comparison if you're evaluating vendors for the same pipeline
  • Scrapfly alternatives — proxy-rotating extraction at scale, used by some price-tracking pipelines
  • n8n integration — wire price monitoring into a no-code workflow with scheduled scrapes
  • Make integration — same data flow, different automation platform
  • Competitor monitoring — go beyond price to track positioning, messaging, and feature releases
  • Brand monitoring — see where your products appear across third-party marketplaces and reviews

Continue exploring

More from Use Cases

View all use cases

Related hubs

Keep the crawl path moving