Shopify Store Products & Prices Scraper avatar

Shopify Store Products & Prices Scraper

Pricing

$0.30 / 1,000 variant scrapeds

Go to Apify Store
Shopify Store Products & Prices Scraper

Shopify Store Products & Prices Scraper

Extract the public product catalogue of any Shopify store: every product and variant with price, compare-at price, SKU, availability, vendor, type, tags, images and dates. Monitor many stores and emit only price changes. One row per variant, $0.30 per 1,000.

Pricing

$0.30 / 1,000 variant scrapeds

Rating

0.0

(0)

Developer

Hriday Rungta

Hriday Rungta

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 hours ago

Last modified

Categories

Share

Turn any Shopify storefront into a clean, structured product feed. Give the Actor a list of store URLs and it returns every product and variant with price, compare-at (strike-through) price, SKU, availability, vendor, type, tags, images and dates - one row per variant, ready for spreadsheets, BI tools or your own code.

Point it at ten competitor stores on a daily schedule with Price changes only switched on and it becomes a price monitor: each run emits only the variants whose price changed since yesterday, with the old and new value side by side.

It reads the public JSON product feed that every Shopify online store exposes (/products.json), so there is no browser, no HTML parsing and no login. Stores that switched the feed off are handled by a sitemap fallback.

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 it extracts

One row per variant (or per product with includeVariants: false):

FieldDescription
storeDomainStorefront hostname
productId, productHandle, productTitleShopify product ID, URL slug, title
vendor, productType, tags[]Brand, merchant-defined type, tags
variantId, variantTitle, variantOptionsVariant ID, e.g. M / Black, and { Size: "M", Color: "Black" }
sku, barcodeSKU; barcode only where the fallback feed provides it
price, compareAtPrice, currencyCurrent price, original price when on sale, ISO currency (from the store's /meta.json or /cart.js; null if neither is exposed)
availablePurchasable right now (true/false)
inventoryQuantity, requiresShipping, gramsStock count is usually null - Shopify's public feed does not publish it
productUrl, variantUrl, imageUrlProduct page, page pre-selected to the variant, main image
images[]All product images (includeImages)
bodyHtml, descriptionRaw HTML and plain-text description (includeBodyHtml)
createdAt, updatedAt, publishedAtISO-8601 timestamps from the store
changeType, priceChange {old,new}, compareAtPriceChange {old,new}Only in newOnly / priceChangesOnly mode
scrapedAtISO-8601

No personal data is collected: storefront catalogues contain products, not people.

Use cases

  • Price monitoring across competitor stores - schedule daily runs in Price changes only mode and get a feed of every price drop, markup, sale start and sale end across dozens of Shopify brands, with old and new prices.
  • DTC market research - map a niche: which vendors, product types and price bands do the leading Shopify brands sell; how big are their catalogues; how often do they publish new products (publishedAt).
  • Dropshipping and product research - pull full catalogues from stores in your niche to compare assortments, spot best-seller tags and find gaps.
  • Retail analytics and repricing - join competitor variants on SKU or barcode with your own catalogue and feed a repricing engine.
  • Agencies and consultants - audit a prospect's catalogue (missing images, compareAtPrice errors, unavailable variants, tag hygiene) before a pitch, or track a client's competitors as a managed service.

Input

FieldTypeDefaultDescription
storeUrlsarray-Store domains or URLs, one per line. Required.
collectionHandlesarrayallOnly these collections (handles or /collections/x URLs), applied to every store.
outputModeall / newOnly / priceChangesOnlyallSee Daily price-change recipe.
maxProductsPerStoreinteger5000Cap per store (Shopify's public feed ends at 25,000).
maxItemsinteger10000Hard cap on saved rows across all stores = cost cap.
includeVariantsbooleantrueOne row per variant; off = one row per product (cheapest variant's price, plus variantCount).
includeImagesbooleanfalseAdd images[].
includeBodyHtmlbooleanfalseAdd bodyHtml and description.
emitAllOnFirstRunbooleanfalseIn change modes, output the whole catalogue on a store's first run as well.
snapshotStoreNamestringshopify-products-snapshotsNamed key-value store holding last-seen prices per store.
requestsPerSecondinteger2Per-store throttle (1-5).
storeConcurrencyinteger3Stores scraped in parallel.
maxRetriesinteger4Backoff retries for network errors, 5xx, 403, 429, 430.
allowHtmlFallbackbooleantrueSitemap / /collections/all fallback for stores that disabled the JSON feed.
proxyConfigurationobjectnoneOptional; 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

{
"storeDomain": "example-socks.com",
"productId": "7654321001",
"productHandle": "merino-crew-sock",
"productTitle": "Merino Crew Sock",
"vendor": "Example Socks Co",
"productType": "Socks",
"tags": ["merino", "crew", "bestseller"],
"variantId": "43000000001",
"variantTitle": "S / Black",
"variantOptions": { "Size": "S", "Color": "Black" },
"sku": "MCS-S-BLK",
"barcode": null,
"price": 18,
"compareAtPrice": 24,
"currency": "USD",
"available": true,
"inventoryQuantity": null,
"requiresShipping": true,
"grams": 85,
"productUrl": "https://example-socks.com/products/merino-crew-sock",
"variantUrl": "https://example-socks.com/products/merino-crew-sock?variant=43000000001",
"imageUrl": "https://cdn.shopify.com/s/files/1/0001/0001/products/mcs-black.jpg?v=1740000000",
"createdAt": "2025-03-01T15:00:00.000Z",
"updatedAt": "2026-09-10T22:22:41.000Z",
"publishedAt": "2025-03-02T14:15:00.000Z",
"scrapedAt": "2026-09-11T06:00:02.114Z"
}

In priceChangesOnly mode each row additionally carries:

{ "changeType": "price-change", "priceChange": { "old": 20, "new": 18 }, "compareAtPriceChange": { "old": 24, "new": 24 } }

Export as JSON, CSV, Excel or XML from the Apify Console or API, or push rows to Google Sheets, Slack, e-mail or a webhook with an integration. The dataset has two built-in views: Variants and Price changes.

Daily price-change recipe

  1. Create a run with your competitor list in storeUrls, outputMode: "priceChangesOnly", maxItems comfortably above your total variant count.
  2. 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 if emitAllOnFirstRun is on.
  3. Save it as a Schedule (e.g. daily at 06:00). From now on every run outputs only:
    • variants whose price or compareAtPrice changed (changeType: "price-change", priceChange: { old, new }), and
    • variants that did not exist before (changeType: "new", priceChange: { old: null, new }).
  4. 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: one variant-scraped event per saved row at $0.0003 ($0.30 per 1,000 rows), no start fee. A 2,000-variant catalogue costs $0.60; a daily price-change run across 20 stores that finds 150 changes costs under 5 cents. maxItems is your hard cost cap and the run also stops when your Apify "max total charge" limit is reached. Unchanged variants, duplicates and skipped stores are never billed.

FAQ

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.

Local development

npm install
npm test # offline unit tests against documented fixtures
APIFY_LOCAL_STORAGE_DIR=$PWD/storage node src/main.js