Shopify Product Data Scraper | Price Changes | $0.3 / 1K
Pricing
$0.30 / 1,000 variant scrapeds
Shopify Product Data Scraper | Price Changes | $0.3 / 1K
Shopify product data from any store, no login: every product and variant with price, compare-at price, SKU, availability, vendor, tags and images. Optional price-change-only mode for competitor monitoring. $0.30 per 1,000 variant rows.
Pricing
$0.30 / 1,000 variant scrapeds
Rating
0.0
(0)
Developer
Hriday Rungta
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
17 days ago
Last modified
Categories
Security
Limited permissionsShare
Get Shopify product data from any store as a flat dataset: every product and variant with price, compare-at price, SKU, stock status, vendor, type, tags, images and dates. Optionally return only the variants whose price changed since your last run. No login, no browser, no API key.
Give the Actor a list of Shopify store URLs. It reads the public product feed every Shopify online store exposes (/products.json) and returns one row per variant, ready for spreadsheets, BI tools or your own code. Stores that switched the feed off are handled by a sitemap fallback. Export to CSV, JSON, Excel or Google Sheets.
Independent tool. Uses the public product feed each Shopify store exposes to shoppers and search engines. Not affiliated with, endorsed by or supported by Shopify Inc. Product data belongs to the individual merchants; you are responsible for respecting each store's terms of service and applicable law.
What you can do with Shopify product data
- Track competitor prices on Shopify stores. Schedule daily runs in
priceChangesOnlymode and get only the price drops, markups, sale starts and sale ends, with old and new price side by side. - Export a Shopify store's full product catalogue to CSV: titles, variants, SKUs, prices, availability, tags and images.
- Watch for new product launches with
newOnlymode, a feed of variants the store did not have before. - Research a niche (DTC brands, dropshipping): vendors, product types, price bands, catalogue sizes and publish dates across many stores.
- Audit a catalogue for missing images, odd compare-at prices, unavailable variants or tag hygiene.
- Join competitor variants on SKU with your own catalogue for repricing or analytics.
Why use this instead of opening /products.json yourself
The feed is public and free. What this Actor adds:
/products.json by hand | This Actor | |
|---|---|---|
| Cost | Free | $0.30 per 1,000 variant rows |
| Paging, 250 products per page, rate limits | You handle | Throttled per store, retried, many stores in parallel |
| Flat variant rows with currency | Nested JSON, no currency | One row per variant, currency from the store |
| Price changes between runs | You build the snapshot logic | priceChangesOnly with old and new values |
| Stores that disabled the feed | Nothing | Sitemap fallback |
Fields in each row
One row per variant (or per product with includeVariants: false):
| Field | Description |
|---|---|
storeDomain | Storefront hostname |
productId, productHandle, productTitle | Shopify product ID, URL slug, title |
vendor, productType, tags[] | Brand, merchant-defined type, tags |
variantId, variantTitle, variantOptions | Variant ID, e.g. M / Black, and { Size: "M", Color: "Black" } |
sku, barcode | SKU; barcode only where the fallback feed provides it |
price, compareAtPrice, currency | Current price, original price when on sale, ISO currency (from the store's /meta.json or /cart.js; null if neither is exposed) |
available | Purchasable right now (true/false) |
inventoryQuantity, requiresShipping, grams | Stock count is usually null - Shopify's public feed does not publish it |
productUrl, variantUrl, imageUrl | Product page, page pre-selected to the variant, main image |
images[] | All product images (includeImages) |
bodyHtml, description | Raw HTML and plain-text description (includeBodyHtml) |
createdAt, updatedAt, publishedAt | ISO-8601 timestamps from the store |
changeType, priceChange {old,new}, compareAtPriceChange {old,new} | Only in newOnly / priceChangesOnly mode |
scrapedAt | ISO-8601 |
No personal data is collected: storefront catalogues contain products, not people.
Input
| Field | Type | Default | Description |
|---|---|---|---|
storeUrls | array | - | Store domains or URLs, one per line. Required. |
collectionHandles | array | all | Only these collections (handles or /collections/x URLs), applied to every store. |
outputMode | all / newOnly / priceChangesOnly | all | See Daily price-change recipe. |
maxProductsPerStore | integer | 5000 | Cap per store (Shopify's public feed ends at 25,000). |
maxItems | integer | 10000 | Hard cap on saved rows across all stores = cost cap. |
includeVariants | boolean | true | One row per variant; off = one row per product (cheapest variant's price, plus variantCount). |
includeImages | boolean | false | Add images[]. |
includeBodyHtml | boolean | false | Add bodyHtml and description. |
emitAllOnFirstRun | boolean | false | In change modes, output the whole catalogue on a store's first run as well. |
snapshotStoreName | string | shopify-products-snapshots | Named key-value store holding last-seen prices per store. |
requestsPerSecond | integer | 2 | Per-store throttle (1-5). |
storeConcurrency | integer | 3 | Stores scraped in parallel. |
maxRetries | integer | 4 | Backoff retries for network errors, 5xx, 403, 429, 430. |
allowHtmlFallback | boolean | true | Sitemap / /collections/all fallback for stores that disabled the JSON feed. |
proxyConfiguration | object | none | Optional; only needed if a store rate-limits you. |
Example - full catalogues of three stores:
{"storeUrls": ["https://www.allbirds.com", "gymshark.com", "https://colourpop.com/collections/lips"],"maxProductsPerStore": 1000,"maxItems": 20000}
Sample output
Real row from a test run on a public Shopify store (tags shortened):
{"storeDomain": "www.allbirds.com","productId": "7340901859408","productHandle": "womens-allbirds-flip-flop-dusty-pink","productTitle": "Women's Allbirds Flip Flop - Dusty Pink","vendor": "Allbirds","productType": "Shoes","tags": ["allbirds::gender => womens", "allbirds::hue => pink", "shoprunner"],"variantId": "42146889039952","variantTitle": "5","variantOptions": { "Size": "5" },"sku": "A12513W050","barcode": null,"price": 25,"compareAtPrice": 50,"currency": "USD","available": false,"inventoryQuantity": null,"requiresShipping": true,"grams": 455,"productUrl": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink","variantUrl": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink?variant=42146889039952","createdAt": "2026-03-27T21:03:50.000Z","updatedAt": "2026-10-21T06:00:13.000Z","publishedAt": "2026-09-25T23:58:13.000Z","scrapedAt": "2026-10-21T06:00:12.583Z"}
imageUrl is also included. In priceChangesOnly mode each row additionally carries:
{ "changeType": "price-change", "priceChange": { "old": 20, "new": 18 }, "compareAtPriceChange": { "old": 24, "new": 24 } }
The dataset has two built-in views: Variants and Price changes.
Daily price-change recipe
- Create a run with your competitor list in
storeUrls,outputMode: "priceChangesOnly",maxItemscomfortably above your total variant count. - Run it once. The first run saves a baseline of every variant's price into the key-value store
shopify-products-snapshots(one record per store) and emits nothing - or the whole catalogue ifemitAllOnFirstRunis on. - Save it as a Schedule (e.g. daily at 06:00). From now on every run outputs only:
- variants whose
priceorcompareAtPricechanged (changeType: "price-change",priceChange: { old, new }), and - variants that did not exist before (
changeType: "new",priceChange: { old: null, new }).
- variants whose
- Add a Slack / e-mail / Google Sheets integration to the schedule to deliver the changes. Unchanged variants cost nothing: you are billed only for rows that are actually saved.
Use newOnly instead to get a feed of new product launches. Use a different snapshotStoreName per watch-list to keep several independent monitors. Variants that vanish from a store are kept in the snapshot, so a temporarily hidden product does not re-trigger as "new" when it returns.
Pricing
Pay per event: $0.0003 per variant row ($0.30 per 1,000). No start fee.
| Run | Rows | Cost |
|---|---|---|
| One store with 2,000 variants | 2,000 | $0.60 |
| Daily price-change run across 20 stores that finds 150 changes | 150 | $0.045 |
Default maxItems | up to 10,000 | up to $3.00 |
maxItems is your hard cost cap, and the run also stops when your Apify "max total charge" limit is reached. Unchanged variants in change modes, duplicates and skipped stores are not billed.
Integrations and scheduling
- Apify Schedules: daily price-change runs are the main use. The snapshot of last-seen prices is kept in a named key-value store in your account.
- Webhooks: call your endpoint when a run finishes.
- Make, n8n, Zapier: send price changes to Slack, email, Airtable or a repricing tool.
- Google Sheets: export the dataset or sync it with the Apify integration.
- API and MCP: call the Actor from code or an AI agent and read JSON.
Limits worth knowing
- Stock levels are usually not available: Shopify's public feed gives
available(in stock or not), not quantities. - Draft, archived and channel-hidden products are not in the feed. The feed ends at 25,000 products per store.
- Headless and password-protected stores often expose no feed and are reported in the run's
STORE-SUMMARIESrecord. - No reviews, metafields or collection membership.
- The default memory is 512 MB. Do not lower it: large catalogues run noticeably slower on less.
FAQ
Is there a Shopify product API without being the store owner?
Shopify's Admin API needs the store owner's credentials. This Actor reads the public storefront feed instead, so it works for stores you do not own, but only returns what shoppers can see.
How do I monitor a competitor's prices on Shopify?
Set outputMode to priceChangesOnly, list the stores, run once for a baseline, then schedule daily runs.
Which stores work?
Any Shopify online store whose /products.json feed is enabled (the default; the vast majority of stores). Headless storefronts (Hydrogen and other custom front ends) and password-protected stores expose no public feed and are reported as feed-disabled / password-protected in the run's STORE-SUMMARIES record. Non-Shopify sites are detected via Shopify's response headers and skipped as not-shopify.
What if a store disabled /products.json?
The Actor reads product handles from the store's sitemap (or /collections/all pages) and fetches each product's /products/{handle}.js feed instead. That costs one request per product, so it is slower; prices and availability are identical, updatedAt is not available in that feed.
Why is currency null for some stores?
It comes from the store's /meta.json (or /cart.js). A few stores block those endpoints. Prices are always in the store's default presentment currency.
Can I get stock levels?
Usually not. Shopify's public feed exposes available (in stock or not) but not quantities; inventoryQuantity is filled only when a store happens to publish it.
How complete is the catalogue?
/products.json returns published products visible to the online store sales channel, 250 per page, up to page 100 (25,000 products). Draft, archived and channel-hidden products are not included. Use collectionHandles to target subsets of very large stores.
Is this legal?
The Actor reads the same public endpoints a browser, Google and price-comparison engines use, without login or circumvention, at a polite rate (2 requests per second per store by default). It collects no personal data. Shopify's own terms bind merchants, not visitors, but individual stores may have their own terms of use: you are responsible for respecting them and for how you use the data. This tool is not affiliated with Shopify Inc.
What about rate limits?
Shopify answers 429 or 430 to aggressive clients. The Actor throttles per store, honours Retry-After, backs off exponentially and rotates proxy sessions if a proxy is configured. Keep requestsPerSecond at 2 unless you know a store tolerates more.
Does it get metafields, reviews or collection membership?
No. Those are not in the public product feed. Collection membership can be derived by running with collectionHandles.
Related Actors
- Shopify Product Scraper by trovevault (third party): another Shopify products Actor.
- Shopify Scraper by autofacts (third party): widely used Shopify products Actor.
- Shopify Store Leads Scraper by clearpath (third party): store contact leads, a different data set.
- App Store Data Scraper (by this developer): app prices and rankings from the Apple App Store.
- Google Maps Places Scraper (by this developer): local business data.