Most scraping content is written from the perspective of the data buyer — analysts aggregating prices for models. This post is for the other side of the counter: sellers. If you run a brand or a third-party seller on Amazon, Walmart, eBay, or your own Shopify store while also retailing elsewhere, the market intelligence you need is a specific, well-defined set of signals about a small set of ASINs/SKUs, collected continuously, and it's a completely different engineering problem from broad crawling. You're not indexing the store; you're monitoring your own neighborhood of it, many times a day, forever.
Here's what a seller-grade monitoring pipeline actually needs to collect, and how to build one that doesn't get your collection infrastructure (or worse, your seller account association) flagged.
The Signal Set: What Sellers Actually Need
For each tracked product, the signals that drive decisions:
- Buy Box ownership and price. On Amazon, the Buy Box price is the market price; the listing page's other offers matter less. Who holds the box — you, a reseller, or Amazon itself — changes hourly.
- Competitor price points across the top ~10 offers, including shipping-inclusive landed price.
- Stock signals. "Only 3 left in stock," "currently unavailable," and — the subtle one — quantity limits on adds-to-cart, which reveal inventory depth without ever showing a count.
- Search rank for your money keywords. Where your product and your top three competitors rank for "stainless steel water bottle 32oz" — collected per marketplace region.
- MAP compliance (minimum advertised price) for brands policing their resellers.
- Review velocity and rating for you and competitors — early-warning for a competitor's new release or a quality problem.
Note what's absent: everything else. A seller monitoring 200 SKUs with 3 competitors each is watching maybe 800 product pages plus some search-result pages. That's a tiny crawl surface — which is good, because it means you can afford high frequency (every 1–2 hours) and careful, polite collection.
Architecture: Small, Frequent, Careful
The classic mistake is treating this like a big crawl. The right mental model is a watchlist daemon: a scheduler that, every N minutes, walks a small set of pages with slow, human-looking pacing, through sessions that look like a shopper in that marketplace.
Three rules from running these pipelines:
One marketplace region = one geo-matched session pool. Amazon serves different prices, availability, and search results by marketplace and by request geography. Collect amazon.de data through German exit IPs, amazon.com through US ones. Beyond block-avoidance, this is data correctness: a US IP pulling amazon.de sees degraded regional content, and your MAP reports will be quietly wrong. I use Thordata residential proxies with a geo suffix and sticky session IDs, one session per monitored marketplace per run.
Pace like a human, not like a batch job. 800 pages per cycle at 5–10 seconds of randomized delay, spread across a handful of sessions, is under two requests per minute per session. That frequency ceiling is your safety margin — respect it even when you're itching for a faster refresh after a price change alert.
Separate collection identity from everything else. Never run collection from an IP or session you also use for seller-account logins or automated selling operations. Marketplaces correlate behavior; keeping monitoring infrastructure completely disjoint from your account infrastructure is cheap insurance.
import requests, random, time
from dataclasses import dataclass
@dataclass
class WatchItem:
asin: str
marketplace: str # "us", "de", "uk"
competitors: list[str]
class SellerWatchDaemon:
def __init__(self, market: str):
self.market = market
self.session = requests.Session()
self.session.proxies = {
"http": f"http://thor-user-sessid-{market}-watch-01-geo-{market}:@proxy.thordata.com:24125",
"https": f"http://thor-user-sessid-{market}-watch-01-geo-{market}:@proxy.thordata.com:24125",
}
def cycle(self, items: list[WatchItem]):
for item in items:
snapshot = self.scrape_product(item.asin)
self.record(item, snapshot)
time.sleep(random.uniform(5, 10)) # human pacing
self.rotate_session_if_needed()
def scrape_product(self, asin: str) -> dict:
url = f"https://www.amazon.{TLD[self.market]}/dp/{asin}"
r = self.session.get(url, headers=HEADERS, timeout=30)
if r.status_code == 503 or "captcha" in r.text[:5000].lower():
self.rotate_session_if_needed(force=True)
return {} # skip, retry next cycle — never hammer
return parse_product_page(r.text) # buybox, offers, stock, price
Parsing the Signals That Aren't Plainly on the Page
The interesting seller signals require interpretation, not just scraping:
Buy box holder. On the offer-listing page, the buy box winner is identified (shipping/sold-by fields). For brand owners, a third-party reseller appearing in your buy box is aMAP-and-distribution problem — flag it, with timestamps.
Landed price. The displayed price often excludes shipping for non-Prime offers. Compute landed price = item price + displayed shipping, or you'll systematically misjudge your competition.
Inventory depth from purchase limits. When Amazon caps quantity ("Limit 3 per customer"), a competitor's stock is nearly exhausted. Track limit appearance/disappearance as a stock-out leading indicator. Similarly, track when an offer vanishes and reappears — that oscillation is a seller running out and restocking, and it's a direct competitive-intelligence signal.
def parse_product_page(html: str) -> dict:
# selectors abbreviated; the point is the derived signals
return {
"buybox_price": css(html, "#corePriceDisplay_desktop_feature_div .a-offscreen"),
"buybox_merchant": css(html, "#sellerProfileTriggerId"),
"availability": css(html, "#availability"),
"qty_limit": parse_qty_limit(html), # regex on "Limit \d+ per"
"offer_count": count_offers(html),
"rating": css(html, 'span[data-hook="rating-out-of-text"]'),
}
def parse_qty_limit(html: str) -> int | None:
m = re.search(r"Limit (\d+) per (?:customer|order)", html)
return int(m.group(1)) if m else None
Search rank. Collect via search-result pages for your keyword set (same geo-matched sessions — search results are the most geo-sensitive surface on any marketplace), recording rank position per keyword per day. Rank is noisy; store daily and analyze on rolling medians.
Detection, Alerts, and the Human Loop
The output of this pipeline isn't data — it's decisions. So build the alerting layer with seller psychology in mind:
- Buy box loss → immediate alert. This is the highest-urgency event; on most marketplaces losing the box costs meaningful sales within the hour.
- Competitor price crossing your price (by threshold, not raw equality — alert on undercut by more than X%, or you'll drown) → alert with a suggested repricer floor.
- Stock-out signals on competitors → opportunity alert. When a competitor's offer shows limit-caps or disappears, raising price or capturing share-behavior matters for a short window.
- MAP violations → daily digest for the brand team, with evidence snapshots (store the raw HTML — you'll want it for reseller conversations).
Store everything as an event log (asin, signal, old_value, new_value, observed_at, evidence_url) — same principle as any monitoring pipeline: append-only history turns a price feed into the ability to answer "when did this start and how fast is it moving," which is what repricing and MAP enforcement decisions actually need.
Frequency, Failure, and Restraint
Last, calibration advice. The temptation with a watchlist daemon is to crank frequency — but every increase multiplies your footprint and your block-rate. In practice, 1-hour cycles for product pages and 1–2 cycles per day for search rank covers nearly every seller decision with hours to spare. When a cycle fails (captcha, 503), the right move is always to back off and retry next cycle, never to retry aggressively — one missed snapshot costs nothing; a flagged session pool costs you visibility for a day. Design the daemon to be boring, and it will still be running next quarter, quietly making you money.
Disclosure: I use Thordata's residential proxies for the geo-matched marketplace monitoring described in this post. If you want to try them, they're at thordata.com, and the code **thor020* gets you 10% off.*
Top comments (0)