Skip to main content
Thirdwatchthirdwatch
E-commerce & products

Monitor Shopify Store Pricing Changes Over Time in 2026

Track price changes, sales, and compare-at discounts across competitor Shopify stores. Automated scheduled runs with diff detection and alerting via Python.

May 26, 2026 · 6 min read · 1,253 words
See the scraper →

Thirdwatch's Shopify Store Scraper extracts current pricing, compare-at prices, sale flags, and variant-level availability from any public Shopify store. Run it on a schedule to build a price history and detect changes — new discounts, price increases, sale events starting or ending, and inventory shifts. No login or API key needed. Built for pricing analysts, ecommerce operators, and competitive intelligence teams who need automated Shopify price monitoring.

Why monitor Shopify store pricing changes

Price is the most dynamic signal in ecommerce. According to Profitwell's 2024 pricing research and Shopify's own documentation on product pricing, DTC brands change prices an average of 2 to 4 times per quarter on core products, with sale events creating additional temporary price drops. On a competitive landscape where your rivals are Shopify-native brands, missing a price change means reacting days or weeks late to a competitor's markdown, launch price adjustment, or permanent price increase.

The job-to-be-done is change detection. A DTC brand manager wants daily alerts when any of five competitors drops prices on overlapping SKUs. A pricing analyst at a marketplace needs to track compare-at-price patterns to understand when rivals run sales and for how long. A reseller monitoring supplier pricing on Shopify stores wants automated notifications when wholesale-to-retail spreads change. An investment analyst tracking a portfolio of DTC brands wants to quantify pricing stability and discount frequency as health indicators. All of these require two things: scheduled data collection and diff logic between snapshots.

How does this compare to the alternatives?

Three approaches to monitoring Shopify store prices:

Approach Cost Reliability Setup time Maintenance
Manual price checks + spreadsheet Free Misses changes between checks Minutes per store per day Unsustainable at scale
Generic price-monitoring SaaS (Prisync, Competera) $99-$499/month subscription Built for marketplace pricing, not Shopify-native 1-2 hours Vendor maintains
Thirdwatch Shopify Store Scraper + scheduled runs Pay per result Variant-level granularity, compare-at tracking 15 minutes Thirdwatch maintains the scraper

Generic price monitoring tools charge monthly subscriptions and are built for marketplace sellers comparing Amazon or eBay listings. They lack Shopify-specific features like compare-at price tracking, variant-level availability, and collection-scoped monitoring. The Shopify Store Scraper returns the raw data you need to build a price monitoring pipeline tuned to your exact competitive set.

How to monitor Shopify pricing in 4 steps

Step 1: How do I set up the baseline snapshot?

Start by pulling the current catalog from the stores you want to monitor. This first run establishes the baseline for future comparisons.

import os, requests, pandas as pd
from datetime import datetime

ACTOR = "thirdwatch~shopify-store-scraper"
TOKEN = os.environ["APIFY_TOKEN"]

WATCHLIST = [
    "https://www.allbirds.com",
    "https://www.everlane.com",
    "https://www.koio.co",
]

resp = requests.post(
    f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
    params={"token": TOKEN},
    json={
        "storeUrls": WATCHLIST,
        "maxProductsPerStore": 1000,
        "includeVariants": True,
    },
    timeout=1200,
)
baseline = pd.DataFrame(resp.json())
baseline["snapshot_date"] = datetime.now().strftime("%Y-%m-%d")
baseline.to_parquet("shopify_prices_baseline.parquet", index=False)
print(f"Baseline: {len(baseline)} products from {baseline.store_domain.nunique()} stores")

Store the baseline as Parquet or CSV. Each subsequent run becomes a new snapshot that you compare against the previous one.

Step 2: How do I detect price changes between snapshots?

Run the scraper again (manually or on schedule) and diff against the previous snapshot by product_id and store_domain.

# Load previous and current snapshots
prev = pd.read_parquet("shopify_prices_baseline.parquet")
curr = pd.DataFrame(resp.json())  # from latest run
curr["snapshot_date"] = datetime.now().strftime("%Y-%m-%d")

# Merge on product_id to compare prices
merged = prev.merge(
    curr,
    on=["store_domain", "product_id"],
    suffixes=("_prev", "_curr"),
    how="outer",
    indicator=True,
)

# Price changes
price_changes = merged[
    (merged["_merge"] == "both")
    & (merged["min_price_prev"] != merged["min_price_curr"])
][["store_domain", "title_curr", "min_price_prev", "min_price_curr",
   "on_sale_prev", "on_sale_curr"]].copy()

price_changes["change_pct"] = (
    (price_changes["min_price_curr"] - price_changes["min_price_prev"])
    / price_changes["min_price_prev"] * 100
).round(1)

# New products (present in current, missing from previous)
new_products = merged[merged["_merge"] == "right_only"][
    ["store_domain", "title_curr", "min_price_curr", "product_type_curr"]
]

# Discontinued products
discontinued = merged[merged["_merge"] == "left_only"][
    ["store_domain", "title_prev", "min_price_prev"]
]

print(f"Price changes: {len(price_changes)}")
print(f"New products: {len(new_products)}")
print(f"Discontinued: {len(discontinued)}")
print(price_changes.sort_values("change_pct"))

The on_sale_prev to on_sale_curr transition catches sale events — when on_sale flips from false to true, the store just started a discount on that product.

Step 3: How do I set up scheduled monitoring with alerts?

Use Apify's scheduling API for automated daily runs, then add a webhook to trigger your alerting logic.

curl -X POST "https://api.apify.com/v2/schedules?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "shopify-price-monitor-daily",
    "cronExpression": "0 7 * * *",
    "timezone": "America/New_York",
    "isEnabled": true,
    "actions": [{
      "type": "RUN_ACTOR",
      "actorId": "thirdwatch~shopify-store-scraper",
      "runInput": {
        "storeUrls": [
          "https://www.allbirds.com",
          "https://www.everlane.com",
          "https://www.koio.co"
        ],
        "maxProductsPerStore": 1000,
        "includeVariants": true
      }
    }]
  }'

Add an ACTOR.RUN.SUCCEEDED webhook that posts to your backend. Your backend downloads the new dataset, runs the diff logic from Step 2, and fires Slack or email alerts for material price changes.

Step 4: How do I track sale event patterns over time?

Accumulate snapshots into a time-series table to analyze sale frequency and duration.

import glob

# Load all historical snapshots
files = sorted(glob.glob("shopify_prices_*.parquet"))
history = pd.concat([pd.read_parquet(f) for f in files])

# Sale event analysis: when did on_sale flip to True?
sale_events = history.groupby(["store_domain", "product_id"]).apply(
    lambda g: g.sort_values("snapshot_date")
    .assign(sale_started=lambda x: x.on_sale & ~x.on_sale.shift(1, fill_value=False))
).reset_index(drop=True)

sale_starts = sale_events[sale_events.sale_started]
print(f"Sale events detected: {len(sale_starts)}")
print(
    sale_starts.groupby("store_domain")
    .agg(total_sale_events=("sale_started", "sum"))
    .sort_values("total_sale_events", ascending=False)
)

Over weeks, this builds a clear picture of each competitor's discounting cadence — how often they run sales, which product types get marked down, and how deep the discounts go (via compare_at_price vs price deltas).

Sample output

A product that moved from full price to sale between two snapshots. The on_sale flag and compare_at_price fields make change detection straightforward.

{
  "store_domain": "everlane.com",
  "store_url": "https://www.everlane.com",
  "url": "https://www.everlane.com/products/mens-premium-weight-crew",
  "product_id": 6934521098,
  "handle": "mens-premium-weight-crew",
  "title": "The Premium-Weight Crew",
  "vendor": "Everlane",
  "product_type": "Tees",
  "min_price": 32.0,
  "max_price": 32.0,
  "min_compare_at_price": 48.0,
  "max_compare_at_price": 48.0,
  "on_sale": true,
  "variants": [
    {
      "id": 41234567,
      "title": "M / Black",
      "sku": "PW-CREW-BLK-M",
      "price": 32.0,
      "compare_at_price": 48.0,
      "available": true,
      "option1": "M",
      "option2": "Black"
    }
  ],
  "variant_count": 15,
  "available": true,
  "updated_at": "2026-05-20T09:15:00Z"
}

The compare_at_price of 48.0 versus the current price of 32.0 shows a 33% markdown. The updated_at timestamp confirms the merchant modified this listing recently, and on_sale: true flags it programmatically without any string parsing.

Common pitfalls

Three issues that surface in Shopify price monitoring pipelines. Variant-level vs product-level tracking — a product's min_price can change because one variant dropped in price while others stayed the same. If you track only min_price, you miss selective markdowns on specific sizes or colors. For precision, diff at the variant level using variants[].id as the key. Phantom price changes from currency rounding — some Shopify stores display prices with rounding that differs from the feed value. A $29.99 product might appear as 29.99 or 30.0 depending on the store's settings. Use a tolerance threshold of 0.50 or 1% in your diff logic to avoid false positives. Missing stores in a run — if a store is temporarily unreachable or password-protected, it produces zero rows. Your diff logic should flag missing store_domain values as anomalies rather than treating all products from that store as discontinued.

The actor handles Shopify's pagination and rate limiting internally. Your monitoring pipeline only needs to manage the diff logic and alerting — the data collection is fully automated.

Related use cases

Frequently asked questions

How quickly does the scraper pick up a price change?

Shopify updates products.json in near real-time when a merchant changes a price. The scraper reads the latest state on every run. With a daily schedule, you catch changes within 24 hours. For faster detection, run every 6 or 12 hours.

Can I track compare-at price changes separately from regular price changes?

Yes. The output includes both min_price and min_compare_at_price per product. Your diff logic can flag when compare_at_price appears or disappears independently of the base price, which indicates the start or end of a sale event.

Does the scraper detect new product launches?

Yes. Any product present in the current run but absent from the previous snapshot is a new launch. The created_at timestamp confirms when the merchant added it. Products that disappear between runs were either unpublished or deleted.

What happens if a store temporarily blocks the scraper?

The actor retries with proxy rotation when useProxy is enabled. If a store consistently blocks requests, it is skipped and no charge applies. Monitoring pipelines should flag missing stores as anomalies rather than silently dropping them.

Can I monitor hundreds of stores at once?

Yes, but split into batches of 10 to 20 stores per run for faster turnaround. Use multiple scheduled runs staggered by 15 minutes to cover a large watchlist without hitting timeout limits.

Related

Try it yourself

100 free credits, no credit card.

About 30 real searches. Add the MCP to Claude or Cursor in two minutes.