Shopify Products Scraper: Prices, Variants and Stock
Pricing
$1.00 / 1,000 product rows
Shopify Products Scraper: Prices, Variants and Stock
Get every product variant of a list of Shopify stores from their public products.json: price, compare-at price and discount, availability, SKU, vendor, type, tags, image, link and dates, plus a price summary per store. Stores that are not Shopify or are closed get one clear error row.
Pricing
$1.00 / 1,000 product rows
Rating
0.0
(0)
Developer
Luigi Fiano
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Paste a list of Shopify stores and get every product variant they sell, one row per variant: product, variant, type, brand, tags, price, compare-at (struck-through) price and discount, availability, SKU, image, link and dates. Each store also gets a summary: how many products, minimum, average and maximum price, and how many are on sale.
The data comes from /products.json, the public product feed every Shopify storefront exposes. No login, no cookies, no browser, nothing bypassed: a store that is not on Shopify, is password protected or blocks the feed gets one clear error row.
What it is for
- Competitor price monitoring: schedule it daily and track prices, discounts and stock of the stores you compete with.
- Brands and distributors: check how resellers price your products (compare-at prices, discounts) and which variants they list.
- Product and market research: full catalogues with types, vendors and tags, to see assortments and price ranges across a niche.
- Agencies and B2B sales to e-commerce: size up a list of prospect stores (catalogue size, price range, share on sale) before you pitch.
- Catalogue exports: a clean table of your own store, with links and images, for audits and feeds.
Input
| Field | What it does | Default |
|---|---|---|
stores | One store per line: a domain or any URL of the store. A /collections/<name> URL reads only that collection | required |
keywords | Keep variants with at least one of these words in the title, variant, type, vendor, tags or SKU | all products |
onlyAvailable | Skip variants that cannot be bought | false |
onlyOnSale | Keep only variants whose compare-at price is higher than the price | false |
oneRowPerProduct | One row per product (cheapest variant, plus variantsCount and priceMax) | false |
includeDescription | Add the product description as plain text | false |
maxProductsPerStore | Stop reading a store after this many products | 10000 |
includeErrorRows | Add one row for each store that cannot be read | true |
concurrency, timeoutSecs, maxRetries | Speed and resilience | 3, 30, 3 |
maxStores, maxResults | Safety caps (0: no limit on rows) | 1000, 0 |
Example:
{"stores": ["shop.example.com", "https://another-store.example/collections/sale"],"onlyAvailable": true,"keywords": ["linen", "summer"]}
Output
One row per variant:
{"type": "variant","store": "shop.example.com","storeName": "Example Shop","product": "Linen Shirt","variant": "S / White","productType": "Shirts","vendor": "Example Brand","tags": ["linen", "summer", "sale"],"price": 75,"compareAtPrice": 100,"discountPercent": 25,"onSale": true,"currency": "EUR","available": true,"sku": "LS-S-W","options": { "Size": "S", "Color": "White" },"image": "https://cdn.shopify.com/s/files/example/white.jpg","url": "https://shop.example.com/products/linen-shirt?variant=11","handle": "linen-shirt","productId": "1","variantId": "11","publishedAt": "2026-10-01T08:00:00.000Z","createdAt": "2026-09-01T08:00:00.000Z","updatedAt": "2026-10-22T08:00:00.000Z","productUpdatedAt": "2026-10-20T08:00:00.000Z","description": null,"collection": null,"input": "shop.example.com","error": null,"scrapedAt": "2026-10-27T08:00:00.000Z"}
priceandcompareAtPriceare numbers in the store's currency.compareAtPriceis filled only when it is higher than the price (the struck-through price shoppers see);onSaleanddiscountPercentfollow from it.currencyandstoreNamecome from the store's public/meta.json; they are null when the store does not publish it.availableis whether the store lets shoppers buy the variant. The public feed does not give stock quantities.updatedAtis the variant's last update (the product's when the variant has none).- Products without variants give one row with null price.
With oneRowPerProduct, each row adds variantsCount and priceMax, and price, compareAtPrice and sku are those of the cheapest variant.
A store that cannot be read gives one error row:
{ "type": "error", "store": "not-a-shop.example", "input": "not-a-shop.example", "collection": null, "error": "products.json not found (HTTP 404): not a Shopify store, or the collection does not exist", "scrapedAt": "2026-10-27T08:00:00.000Z" }
Other errors you can see: the store is password protected: nothing was scraped, access denied (HTTP 403): the store blocks its public product feed, nothing was bypassed, not a Shopify store: /products.json returned an HTML page, rate limited by the store (HTTP 429), request failed: timeout.
Summary per store
The SUMMARY record in the key-value store has run totals and one entry per store:
{"store": "shop.example.com","storeName": "Example Shop","currency": "EUR","collection": null,"pages": 2,"productsScanned": 312,"products": 312,"rows": 1048,"availableRows": 951,"onSaleRows": 120,"onSaleProducts": 37,"minPrice": 9.9,"avgPrice": 64.37,"maxPrice": 289,"error": null,"stoppedEarly": null}
products, prices and on-sale counts refer to the rows delivered, so they follow your filters. productsScanned counts every product read.
Pricing
Pay per result: $0.001 per row delivered to the dataset ($1 per 1,000 rows), plus the small Apify platform usage of the run. Variants removed by your filters are not delivered and not charged. Use oneRowPerProduct for one row per product. Each error row counts as one result: turn off includeErrorRows to get errors only in the summary.
Notes
- Only public data: the
/products.jsonand/meta.jsonendpoints of the storefront. No login, no cookies, no proxies, no CAPTCHA solving. Password-protected or blocked stores are reported, not bypassed. - No personal data: rows contain product data only. From
/meta.jsononly the store name and currency are kept. - Pages of one store are read one after the other with a short pause, 250 products per page, and Retry-After is honoured, to stay gentle with each store.
- Some stores turn the public feed off or limit how many pages it returns: the summary tells you when a store stopped early.
- Not affiliated with Shopify.