Shopify Products Scraper: Prices, Variants and Stock avatar

Shopify Products Scraper: Prices, Variants and Stock

Pricing

$1.00 / 1,000 product rows

Go to Apify Store
Shopify Products Scraper: Prices, Variants and Stock

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

Luigi Fiano

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

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

FieldWhat it doesDefault
storesOne store per line: a domain or any URL of the store. A /collections/<name> URL reads only that collectionrequired
keywordsKeep variants with at least one of these words in the title, variant, type, vendor, tags or SKUall products
onlyAvailableSkip variants that cannot be boughtfalse
onlyOnSaleKeep only variants whose compare-at price is higher than the pricefalse
oneRowPerProductOne row per product (cheapest variant, plus variantsCount and priceMax)false
includeDescriptionAdd the product description as plain textfalse
maxProductsPerStoreStop reading a store after this many products10000
includeErrorRowsAdd one row for each store that cannot be readtrue
concurrency, timeoutSecs, maxRetriesSpeed and resilience3, 30, 3
maxStores, maxResultsSafety 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"
}
  • price and compareAtPrice are numbers in the store's currency. compareAtPrice is filled only when it is higher than the price (the struck-through price shoppers see); onSale and discountPercent follow from it.
  • currency and storeName come from the store's public /meta.json; they are null when the store does not publish it.
  • available is whether the store lets shoppers buy the variant. The public feed does not give stock quantities.
  • updatedAt is 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.json and /meta.json endpoints 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.json only 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.