Web and domains

Shopify Price Tracker: Catalog and Changes as JSON

Every Shopify storefront serves its public catalog as JSON at /products.json. This Actor reads it for one store or many, writes one clean record per variant or product, and in changes mode reports only what moved since the last run: prices, stock and new or removed products.

Open the Actor on Apify Store

Shopify Catalog Monitor & Price Tracker — Products Scraper. Pay per result; see the Store page for current pricing.

The source and its limits

Shopify storefronts expose /products.json (and /collections.json) publicly. The Actor paginates each store 250 products at a time, normalizes prices to numbers, builds canonical product URLs and stamps each record with fetchedAt. No browser, proxies or API keys.

In changes mode it keeps the previous snapshot of each store in a key-value store you name and writes only what changed, with before and after values and the percentage price move. Change types are new-product, removed-product, new-variant, removed-variant, price-changed, availability-changed and title-changed.

Limits worth knowing

  • Requests to each store are spaced about 500 ms apart, so a 5,000-product store takes roughly 15 to 20 seconds.
  • Most stores do not expose inventory quantities; inventoryQuantity is null unless the store publishes it.
  • Password-protected stores, stores not on Shopify, and stores that block datacenter traffic return an error item. The Actor does not use residential proxies or browsers to get around that.
  • When maxProductsPerStore truncates a listing, removals are not reported for that store, to avoid false "removed" events.

Fields you get

FieldWhat it holds
storeDomain, productId, productHandle, productTitleThe store and product
vendor, productType, tagsAs set by the store
variantId, variantTitle, sku, barcode, option1-3The variant
price, compareAtPriceNumbers in the store's currency
available, inventoryQuantityAvailability; quantity only where the store publishes it
productUrl, imageUrl, gramsCanonical URL, image and weight
createdAt, updatedAt, publishedAtTimestamps from Shopify
recordType, changeType, before, after, percentChangeIn changes mode

Example output

A price-changed record in changes mode (from the Actor's README; illustrative ids):

{
  "recordType": "change",
  "changeType": "price-changed",
  "changedFields": [
    "price"
  ],
  "before": {
    "price": 98,
    "compareAtPrice": 120
  },
  "after": {
    "price": 88.2,
    "compareAtPrice": 120
  },
  "percentChange": -10,
  "detectedAt": "2026-10-17T12:00:00.000Z",
  "storeDomain": "www.allbirds.com",
  "productTitle": "Wool Runner",
  "variantTitle": "Natural Grey / 9",
  "price": 88.2,
  "available": true,
  "productUrl": "https://www.allbirds.com/products/wool-runner"
}

Use cases

Competitor price tracking

Run changes mode daily on a set of competitor stores and send price-changed events, with the percentage move, to a webhook.

Supplier stock watch

Watch availability-changed events on a supplier's store to know when variants go out of stock or come back.

Catalog export

Snapshot mode exports every variant with price, SKU, options and image, for analysis or a product feed.

How to run it

You need an Apify account and its API token (Console → Settings → API & Integrations). Each run is billed per result to your account; set a maximum total charge for the run to cap spend.

With curl

The sync endpoint starts a run, waits up to 300 seconds and returns the dataset items. Price and stock changes on one store (the first run stores a baseline):

export APIFY_TOKEN=<your Apify API token>

curl -sS -X POST \
  "https://api.apify.com/v2/acts/owlabee~shopify-catalog-monitor/run-sync-get-dataset-items" \
  -H "Authorization: Bearer ${APIFY_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
  "storeUrls": [
    "www.deathwishcoffee.com"
  ],
  "mode": "changes",
  "granularity": "variant",
  "maxProductsPerStore": 30,
  "includeCollections": false,
  "stateStoreName": "shopify-price-monitor-deathwishcoffee"
}'

From an AI agent (MCP)

Apify hosts an MCP server. This config exposes only this Actor to the agent:

{
  "mcpServers": {
    "owlabee-shopify-catalog-monitor": {
      "url": "https://mcp.apify.com/?tools=owlabee/shopify-catalog-monitor",
      "headers": {
        "Authorization": "Bearer ${APIFY_TOKEN}"
      }
    }
  }
}

Then ask, for example: Track the prices on www.deathwishcoffee.com and tell me which variants changed price or went out of stock since yesterday.

Python, Node.js and more examples

Runnable curl.sh, python.py, node.mjs and mcp.json files for this Actor are in examples/shopify-catalog-monitor on GitHub.

No code: Zapier, Make or n8n

Apify's official apps on these platforms can run this Actor and pass its results on. Step-by-step recipes: Competitor Shopify Price Changes to Slack with Make.

FAQ

Does it need a Shopify API key?

No. It reads the public products.json endpoint that Shopify serves for storefronts.

Why does the first changes run return so many records?

There is nothing to compare against yet, so the full catalog is stored as baseline records. Later runs return only changes.

What counts as a change?

Price, compare-at price, availability, product title and variant title, plus products and variants that appear or disappear. A new product is one event, not one per variant.

Can I track stores that are not on Shopify?

No. For other sites, the sitemap change monitor reports page and content changes.

How do I start a fresh baseline?

Use a new stateStoreName, or delete the store's key in the existing one.