All data workflows
E-COMMERCE DATA · WORKFLOW GUIDE

Compare e-commerce prices with variant and stock checks

A market-comparison worksheet that keeps variant, currency, stock status, and retrieval failure separate

E-commerce data workflow illustration

Quick answer

A price-monitoring pilot should compare the same product variant in a named market, with currency, observation time, and stock status recorded for every row. Keep unknown availability and failed fetches separate from out-of-stock observations, and require human review before repricing. The sample is synthetic; no scraper or repricer is included.

Workflow at a glance

  1. Define the offer set

    List product URLs, exact variants, markets, price basis, refresh interval, expected volume, and output format

  2. Agree acceptance rules

    Match unit or pack size, currency, timestamp, market, stock state, and review treatment for failed pages

  3. Review before repricing

    Compare accepted observations against the worksheet; no repricer or managed scraping service is promised

SYNTHETIC SCHEMA EXAMPLE

Sample data

Synthetic records show a comparable offer, an uncertain price, and a failed fetch. These fictional records demonstrate the format, not measured service output.

{
    "product_url": "https://example.com/products/linen-shirt",
    "variant": "M / blue",
    "price": 29,
    "currency": "USD",
    "availability": "in_stock",
    "observed_at": "2026-09-17T10:00:00Z",
    "region": "US",
    "match_status": "accepted"
}

Fit the job before collecting prices

Use this workflow when a pricing or e-commerce team needs comparable observations from a defined source list. A product URL alone is not a match: size, pack count, seller, region, and price basis can change the offer being compared.

This page proposes a data contract for a pilot discussion. It does not promise page access, coverage, a managed scraper, an automatic repricer, a service-level agreement, or a delivery date. Check the target's current terms and access rules before collecting data; a proxy supplies a network route, not permission.

Define comparable observations

Field Acceptance check
product_url HTTPS URL on the approved source list
variant Same size, pack, seller, and product identifier required by the comparison
price, currency, and region Finite non-negative amount; the sample's synthetic market/currency pair must match the local map
availability in_stock, out_of_stock, or unknown; a failed fetch is a separate exception
observed_at and region Timezone-aware timestamp and named market
match_status accepted only when all required comparison fields pass

In this synthetic example only, the local map is US → USD and DE → EUR. It is not a general market database. For real observations, agree the market/currency contract and have a reviewer verify the source currency; the sample validator cannot determine the correct currency for a real market. An unknown stock value is not an out-of-stock result. A failed fetch is not a price change. Keep tax, shipping, promotion, seller, and variant rules in the acceptance worksheet whenever they affect the comparison.

Validate the synthetic JSON

Download the synthetic JSON sample or CSV sample, and use the pilot brief to prepare implementation questions. Save the JSON as sample.json, then save the following code as validate-sample.py beside it. Run it with Python 3.12.0, the version used for this local check. Use the official Python downloads page. The validator uses the standard library; the json documentation covers file parsing.

python3 --version
python3 validate-sample.py

The validator checks required values, synthetic hosts, timestamp offsets, valid numeric prices, and the distinction between failed, unknown, and accepted records. The validator sends malformed URLs and values of the wrong type to review rather than treating them as accepted. It still checks only this synthetic record contract; it does not verify a live source or make a service request.

import json
import math
from datetime import datetime
from pathlib import Path
from urllib.parse import urlsplit

SAMPLE_PATH = Path("sample.json")
ALLOWED_HOSTS = {"example.com", "example.org"}


def is_synthetic_https_url(value):
    if not isinstance(value, str):
        return False
    try:
        parsed_url = urlsplit(value)
        hostname = parsed_url.hostname
        parsed_url.port
        username = parsed_url.username
        password = parsed_url.password
    except ValueError:
        return False
    return (parsed_url.scheme == "https"
            and hostname in ALLOWED_HOSTS
            and username is None
            and password is None)
REQUIRED_FIELDS = {
    "product_url",
    "variant",
    "price",
    "currency",
    "availability",
    "observed_at",
    "region",
    "match_status",
}
ALLOWED_STATUSES = {"accepted", "review", "unknown"}
ALLOWED_AVAILABILITY = {"in_stock", "out_of_stock", "unknown"}
SYNTHETIC_CURRENCY_BY_REGION = {"US": "USD", "DE": "EUR"}

sample = json.loads(SAMPLE_PATH.read_text(encoding="utf-8"))
if sample.get("sample_type") != "synthetic":
    raise ValueError("Expected a synthetic schema example")

records = sample.get("records")
if not isinstance(records, list) or not records:
    raise ValueError("Expected at least one record")

for index, record in enumerate(records, start=1):
    issues = []
    if not isinstance(record, dict):
        print(f"record {index}: review; row is not an object")
        continue

    missing = sorted(REQUIRED_FIELDS - record.keys())
    if missing:
        issues.append("missing " + ", ".join(missing))

    product_url = record.get("product_url")
    if not isinstance(product_url, str):
        issues.append("product_url must be text")
    else:
        if not is_synthetic_https_url(product_url):
            issues.append("product_url is outside the synthetic HTTPS hosts")

    variant = record.get("variant")
    currency = record.get("currency")
    region = record.get("region")
    if not all(isinstance(value, str) and value.strip()
               for value in (variant, currency, region)):
        issues.append("variant, currency, and region must be non-empty text")
    else:
        if (len(currency) != 3 or not currency.isascii()
                or not currency.isalpha() or currency != currency.upper()):
            issues.append("currency must be a three-letter uppercase code")
        expected_currency = SYNTHETIC_CURRENCY_BY_REGION.get(region)
        if expected_currency is None:
            issues.append("region is outside the synthetic market map")
        elif currency != expected_currency:
            issues.append("currency does not match the synthetic market map")

    observed_at = record.get("observed_at")
    try:
        if not isinstance(observed_at, str):
            raise ValueError
        timestamp = datetime.fromisoformat(observed_at.replace("Z", "+00:00"))
        if timestamp.tzinfo is None or timestamp.utcoffset() is None:
            issues.append("observed_at needs a timezone")
    except ValueError:
        issues.append("observed_at is not a timezone-aware ISO timestamp")

    price = record.get("price")
    valid_price = (isinstance(price, (int, float))
                   and not isinstance(price, bool)
                   and math.isfinite(price)
                   and price >= 0)
    if price is not None and not valid_price:
        issues.append("price must be a finite non-negative number or null")

    availability = record.get("availability")
    if not isinstance(availability, str) or availability not in ALLOWED_AVAILABILITY:
        issues.append("availability is invalid")
    status = record.get("match_status")
    if not isinstance(status, str) or status not in ALLOWED_STATUSES:
        issues.append("match_status is invalid")
    elif status != "accepted" and record.get("fetch_status") != "failed" and not record.get("review_reason"):
        issues.append("non-accepted row needs review_reason")

    if record.get("fetch_status") == "failed":
        issues.append("fetch failed")
    elif status == "accepted" and (
            price is None or availability not in {"in_stock", "out_of_stock"}):
        issues.append("accepted row needs a price and known stock state")

    outcome = "accepted" if status == "accepted" and not issues else "review"
    print(f"record {index}: {outcome}")
    for issue in issues:
        print(f"  reason: {issue}")

Expected output:

record 1: accepted
record 2: review
record 3: review
  reason: fetch failed

The null price in the sample means there is no observation to accept. The validator allows it for rows held for review and never fills a missing value. A local pass checks only the synthetic schema; it cannot prove product matching, source accuracy, representativeness, or an economic result. Python's datetime documentation explains aware timestamps, URL parsing documentation says parsing alone is not validation, and math.isfinite detects infinite or NaN values.

Agree the pilot measurement

Specify the source list, exact variants, markets, currency, price basis, refresh interval, volume, output format, and exception budget. Decide how to handle taxes, shipping, seller changes, promotions, duplicate offers, and delayed observations before comparison starts.

Measure only after defining the baseline, review period, eligible offers, and cost boundary. The sample contains no live prices or savings evidence. It cannot support a margin, coverage, or repricing-performance claim.

Keep people in control of repricing

Compare every accepted observation with the target product and agreed price basis before changing a live price. The sample does not trigger a price change, and the proposed workflow grants no access to the buyer's store or repricing system.

For a feasibility discussion, put source URLs, variants, markets, fields, volume, refresh interval, and acceptance rules in the workflow email draft addressed to hello@proxylane.dev. It opens for review and is not sent automatically; ProxyLane must confirm feasibility, scope, price, and acceptance criteria before any work.

ProxyLaneTraffic never expires

Premium residential proxies
From $2/GB

Run this workflow with the tools you already use. Rotating or sticky sessions up to 72 hours.

Try 350 MB for $1.95 View plans Discuss managed data

Keep reading

Scope a reviewable data pilot for AI retrieval

Define an AI retrieval pilot with source URLs, freshness rules, failed-page handling, and a synthetic JSON validator. No retrieval API is included.

Read guide

Enrich CRM company records from supplied domains

Scope CRM company enrichment from supplied domains with source URLs, timestamps, and a review queue for ambiguous matches. Includes a synthetic JSON check.

Read guide

Scope people sourcing with source-backed identity review

Scope public professional profile sourcing with source evidence, timestamps, and human identity review. Includes privacy limits and a synthetic sample validator.

Read guide