Shopify Product Data Scraper | Price Changes | $0.3 / 1K avatar

Shopify Product Data Scraper | Price Changes | $0.3 / 1K

Pricing

$0.30 / 1,000 variant scrapeds

Go to Apify Store
Shopify Product Data Scraper | Price Changes | $0.3 / 1K

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

Hriday Rungta

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

17 days ago

Last modified

Security

Limited permissions

Share

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 priceChangesOnly mode 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 newOnly mode, 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 handThis Actor
CostFree$0.30 per 1,000 variant rows
Paging, 250 products per page, rate limitsYou handleThrottled per store, retried, many stores in parallel
Flat variant rows with currencyNested JSON, no currencyOne row per variant, currency from the store
Price changes between runsYou build the snapshot logicpriceChangesOnly with old and new values
Stores that disabled the feedNothingSitemap fallback

Fields in each row

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.

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

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

  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: $0.0003 per variant row ($0.30 per 1,000). No start fee.

RunRowsCost
One store with 2,000 variants2,000$0.60
Daily price-change run across 20 stores that finds 150 changes150$0.045
Default maxItemsup to 10,000up 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-SUMMARIES record.
  • 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.

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.