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.
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;
inventoryQuantityis 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
maxProductsPerStoretruncates a listing, removals are not reported for that store, to avoid false "removed" events.
Fields you get
| Field | What it holds |
|---|---|
storeDomain, productId, productHandle, productTitle | The store and product |
vendor, productType, tags | As set by the store |
variantId, variantTitle, sku, barcode, option1-3 | The variant |
price, compareAtPrice | Numbers in the store's currency |
available, inventoryQuantity | Availability; quantity only where the store publishes it |
productUrl, imageUrl, grams | Canonical URL, image and weight |
createdAt, updatedAt, publishedAt | Timestamps from Shopify |
recordType, changeType, before, after, percentChange | In 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.