Skip to content
Web Scraping APIs

How to Scrape Prices and Build a Price Monitor

Pim

Pim · Scrappey Research

June 8, 2026 4 min read

How to Scrape Prices and Build a Price Monitor — conceptual illustration
On this page

To scrape prices reliably you fetch each product page through a residential proxy in the right country, parse the current price out of the page (or let a scraping API return it as structured data), store each reading with a timestamp so you have history, and re-check on a schedule. The mechanics are simple; the hard part is that price pages defend against automated traffic, prices are localized by country and currency, and the price you want (sale price, not the struck-through original) often loads via JavaScript. A code-first web scraping API handles the fetch layer - JavaScript rendering, rotating residential IPs, and browser verification - so your code only deals with parsing, storage, and scheduling.

Quick facts

Five moving partsTargets, reliable fetch, price parse, history store, schedule + alerts
Why it is hardActive blocking, per-country prices/currency, JS-rendered sale prices
Fetch layerResidential proxy in the buyer geo + JS rendering, one API call
CadenceHourly for hot SKUs, daily for the long tail
StoreOne timestamped row per reading - never overwrite, keep history

The five parts of a price monitor

A price monitor is five small jobs wired together: pick targets, fetch the page, parse the price, store the reading, and schedule plus alert.

1. Pick targets. Keep a list of product URLs (or marketplace identifiers) you care about. Tag each as hot (fast-moving, competitive SKUs) or long tail.

2. Fetch reliably. This is where DIY trackers break. A plain request from a datacenter IP gets blocked, gets the wrong country price, or misses a price that loads via JavaScript. Route each request through a residential proxy in the buyer geography and render the page. A scraping API does all of this in one call.

3. Parse the price. Pull the current price, not the struck-through original. Many stores expose a clean price in embedded JSON (schema.org Product/Offer markup) which is more stable than scraping a styled price element; auto-parse output or that JSON block is your friend.

4. Store history. Append a timestamped row per reading - never overwrite. History is what lets you draw trends and detect drops.

5. Schedule and alert. Re-run on a cadence and compare to the last stored value to fire alerts. See the next two sections.

Fetch and parse: residential proxies, geo, and the right number

The fetch step decides whether your monitor works at all, because retailers challenge automated traffic and localize prices.

Geo and currency. A product page served to a US IP and a German IP can show different prices, currency, and availability. Set the proxy country to match the market you sell in - if you track US pricing, fetch from a US residential IP. Getting this wrong silently poisons your data.

Reliability at scale. Price pages on large retailers sit behind verification challenges and rate limits, so a naive monitor returns errors instead of prices. A managed fetch layer that handles residential IP routing, browser rendering, and per-site request pacing keeps the monitor stable and the data complete. Holding a consistent session per target keeps each product's readings on one coherent connection, which makes the collected price series cleaner.

Grabbing the correct number. Prefer structured data: most stores embed an application/ld+json Product object with a clean offers.price and priceCurrency. That survives layout redesigns far better than a CSS selector aimed at a price element. With autoparse set to true, Scrappey returns parsed fields for many product pages; otherwise parse the embedded JSON yourself. Normalize to a number and store the currency alongside it.

Schedule, store history, and alert

Re-check on a cadence, append every reading to durable storage, and compare each new price to the last one to decide when to alert.

Cadence. Tier your SKUs. Hot, competitive items justify hourly checks; the long tail is fine daily or weekly. Polite, tiered cadence costs less and is far gentler on the target site than hammering every URL every minute - see throttling and polite crawling. Run the loop from cron, a scheduler, or a queue worker with 200 concurrent requests available on every Scrappey plan.

Store history. Each run writes one row per SKU: url, price, currency, timestamp. A timestamped table (SQLite, Postgres, or even CSV to start) gives you trend charts and an audit trail. You can later export to CSV/JSON or push to Google Sheets for the business team.

Alert. After storing, compare the new price to the previous stored value. Fire a webhook, email, or Slack message when it crosses a threshold (drops more than X percent, or undercuts your own price). That diff-on-write is the entire alerting engine. For a survey of off-the-shelf options, see the price-monitoring tools roundup.

Code example

python
import json
import re
import requests

API = 'https://publisher.scrappey.com/api/v1?key=YOUR_API_KEY'

# A few product pages to watch. Tag cadence in your own scheduler.
TARGETS = [
    'https://www.example-store.com/products/widget-pro',
    'https://www.example-store.com/products/widget-lite',
]


def extract_price(html):
    """Prefer schema.org Product JSON-LD; it survives redesigns."""
    pattern = r'<script[^>]+ld\+json[^>]*>(.*?)</script>'
    for block in re.findall(pattern, html, re.S):
        try:
            data = json.loads(block)
        except ValueError:
            continue
        items = data if isinstance(data, list) else [data]
        for item in items:
            offers = item.get('offers') if isinstance(item, dict) else None
            if isinstance(offers, list):
                offers = offers[0] if offers else None
            if isinstance(offers, dict) and offers.get('price'):
                return float(offers['price']), offers.get('priceCurrency', '')
    return None, None


def fetch_price(url):
    resp = requests.post(API, json={
        'cmd': 'request.get',
        'url': url,
        'proxyCountry': 'UnitedStates',   # match the market you sell in
        'session': 'price-monitor-us',    # reuse IP + cookies per target
        'autoparse': True,
    })
    html = resp.json()['solution']['response']
    return extract_price(html)


if __name__ == '__main__':
    for url in TARGETS:
        price, currency = fetch_price(url)
        # Append one timestamped row per reading - keep full history.
        print(url, price, currency)

Related terms

Concept map

Concept map

How How to Scrape Prices: Build a Price Monitor That Survives Anti-Bot connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Web Scraping APIs
Building map…

Frequently asked questions

How do I scrape the sale price and not the original crossed-out price?

Read the structured data first. Most product pages embed a schema.org Product object in an application/ld+json script tag with a single offers.price field that already reflects the current sale price. That is far more reliable than targeting a styled price element, where the original and discounted prices both appear in the markup and layout changes break your selector.

How often should I re-check prices?

Tier your cadence. Hourly per SKU is realistic for hot, competitive items when you rotate residential proxies and reuse a session per target; the long tail is fine daily or weekly. Hammering every URL every minute from one IP is unreliable and wastes requests, so pace per site and only check fast-moving items frequently.

Do I need a different proxy country for each market I track?

Yes. Many stores localize price, currency, and availability by the country they detect from your IP, so scraping a US store from a European IP returns misleading numbers. Set the proxy country to the market you sell in for each target, and store the currency next to every price so you never mix markets.

Last updated: 2026-06-08

'\n for block in re.findall(pattern, html, re.S):\n try:\n data = json.loads(block)\n except ValueError:\n continue\n items = data if isinstance(data, list) else [data]\n for item in items:\n offers = item.get('offers') if isinstance(item, dict) else None\n if isinstance(offers, list):\n offers = offers[0] if offers else None\n if isinstance(offers, dict) and offers.get('price'):\n return float(offers['price']), offers.get('priceCurrency', '')\n return None, None\n\n\ndef fetch_price(url):\n resp = requests.post(API, json={\n 'cmd': 'request.get',\n 'url': url,\n 'proxyCountry': 'UnitedStates', # match the market you sell in\n 'session': 'price-monitor-us', # reuse IP + cookies per target\n 'autoparse': True,\n })\n html = resp.json()['solution']['response']\n return extract_price(html)\n\n\nif __name__ == '__main__':\n for url in TARGETS:\n price, currency = fetch_price(url)\n # Append one timestamped row per reading - keep full history.\n print(url, price, currency)"},"relatedSlugs":["best-scraping-api-for-ecommerce-price-monitoring","what-is-a-web-scraping-api","what-is-a-residential-proxy","dynamic-content-scraping","export-scraped-data-to-csv-json","what-is-anti-bot-detection","web-scraping-to-google-sheets","is-web-scraping-legal","scrape-website-data-to-excel","what-is-javascript-rendering","best-scraping-api-for-real-estate-data"],"faq":[{"q":"How do I scrape the sale price and not the original crossed-out price?","a":"Read the structured data first. Most product pages embed a schema.org Product object in an application/ld+json script tag with a single offers.price field that already reflects the current sale price. That is far more reliable than targeting a styled price element, where the original and discounted prices both appear in the markup and layout changes break your selector."},{"q":"How often should I re-check prices?","a":"Tier your cadence. Hourly per SKU is realistic for hot, competitive items when you rotate residential proxies and reuse a session per target; the long tail is fine daily or weekly. Hammering every URL every minute from one IP is unreliable and wastes requests, so pace per site and only check fast-moving items frequently."},{"q":"Do I need a different proxy country for each market I track?","a":"Yes. Many stores localize price, currency, and availability by the country they detect from your IP, so scraping a US store from a European IP returns misleading numbers. Set the proxy country to the market you sell in for each target, and store the currency next to every price so you never mix markets."}],"updatedAt":"2026-06-08"}}