Shopify Products Scraper: Prices, Stock & SKUs · Apify
Shopify Store Products Scraper avatar

Shopify Store Products Scraper

Pricing

from $0.60 / 1,000 results

Go to Apify Store
Shopify Store Products Scraper

Shopify Store Products Scraper

Shopify products scraper for catalog and variant exports: prices, discounts, SKUs, availability and images. Export CSV or JSON from public store feeds, with one row per product or variant. Review run summaries for failures and budget-limited exports. No Shopify API key required.

Pricing

from $0.60 / 1,000 results

Rating

0.0

(0)

Developer

Murat Uzun

Murat Uzun

Maintained by Community

Actor stats

0

Bookmarked

9

Total users

3

Monthly active users

19 hours ago

Last modified

Features

Limited permissions

Share

What is Shopify Store Products Scraper?

Shopify Store Products Scraper exports a public Shopify catalog as CSV, Excel or JSON, with prices, discounts, SKUs, barcodes, real stock counts (where the store publishes them), variant options and images, for $1 per 1,000 products with every variant nested in the product row and no add-on fee for stock or barcodes. Built for e-commerce analysts, dropshippers and price-monitoring teams. Enter a store URL and choose one row per product or one row per size/color variant. No Shopify API key is required.

Use it to prepare a catalog spreadsheet, compare product prices, or collect SKU-level availability snapshots for your own monitoring workflow. It reads the store's public products.json feed directly. When a store blocks or disables that feed (common on headless Shopify storefronts such as Hydrogen, which answer 403/404), it falls back to the store sitemap and each product page's schema.org structured data; those rows are marked dataSource: "jsonld". Real stock counts and barcodes are read from each product's public JSON at no extra charge when the store publishes them; inventoryAccuracy says whether a count is exact, partial, not published or not checked. Currency is never inferred.

Start with a small export

  1. Enter one store URL, such as https://www.allbirds.com.
  2. Set maxProductsPerStore to 2. Leave includeVariants off for two product rows that already contain every variant, or turn it on for one flat row per variant (spreadsheet-friendly, one charge per variant).
  3. Run the Actor, inspect the dataset, and export CSV or JSON.
  4. Read the OUTPUT summary before increasing the limit or replacing an existing catalog. Product limits, filters, failures and budget limits can leave the export incomplete. Never infer that a missing SKU has been deleted from a partial export.

For repeated snapshots, match rows by store plus productId (product mode) or variantId (variant mode), or turn on monitorChanges to let the Actor output only what changed since the previous run. It does not send alerts itself; connect a webhook or integration to the dataset for notifications.

Monitor price and stock changes

Turn on monitorChanges and schedule the run with the same input:

  1. The first run saves a baseline and outputs no rows. OUTPUT.monitorMessages shows baseline saved N.
  2. Each later run outputs only changed items, with the normal row fields plus changeType, previousPrice and previousAvailable. An unchanged store outputs 0 rows and incurs no result charges; the start fee still applies.
  3. changeType is new, price_drop, price_increase, back_in_stock, sold_out or removed. When stock and price both change, the stock change is reported; both previous values are included. A removed row has price and available set to null. Set includeRemoved to false to skip these rows.

Snapshots are stored in your named key-value store shopify-products-scraper-state, one record per store and scope: collectionHandles, includeVariants, onlyAvailable, maxProductsPerStore and the optional trackingId. Changing any of them starts a new baseline. Variant mode compares each variant's price and available; product mode compares minPrice and anyAvailable. With onlyAvailable on, an item that sells out is no longer returned and is reported as removed.

A failed, empty, invalid or budget-limited store result is not compared: it outputs no change rows, reports no removals, leaves the snapshot unchanged and is listed in OUTPUT.comparisonSkipped with a reason. The snapshot is saved only after the dataset write is confirmed. If the run's spending limit truncates change rows, the unwritten changes are reported again on the next run. A store larger than maxProductsPerStore is compared only within the products returned under that limit, so raise the limit to cover the whole catalog. Variant-mode monitoring of a headless store read through the structured-data fallback is also skipped (variant_ids_unavailable), because those pages publish no Shopify variant ID; use product mode to monitor such stores.

What data does Shopify Store Products Scraper extract?

Shopify Store Products Scraper returns the fields below according to the selected product or variant mode. Fields not exposed by the public feed remain null:

FieldTypeDescription
store, productId, productHandle, productUrlstring, number, stringWhich store the row came from and the product's id, slug and live URL
title, vendor, productType, tagsstring, arrayProduct title, brand, merchant category and tags
createdAt, updatedAt, publishedAtstringProduct lifecycle dates (ISO 8601)
variantId, variantTitle, sku, barcodenumber, stringVariant identity (variant mode)
price, compareAtPrice, currency, discountPercentnumber, stringSelling price, list price, store base currency from metadata and % off; unknown currency stays null
available, inventoryQuantityboolean, numberIn-stock status and real units on hand per variant, read from the product's public JSON when the store publishes it
inventoryManagement, inventoryPolicy, inventoryCount, inventoryAccuracystring, numberStock tracking system, oversell policy (deny/continue), summed product stock (product mode) and the evidence level: exact, partial, not_published or not_checked
option1, option2, option3stringVariant option values, e.g. color / size
weightGrams, requiresShippingnumber, booleanShipping weight and whether the variant ships physically
imageUrl, imageUrls, imageCount, bodyTextstring, array, number, stringPrimary/variant image, all distinct usable product image URLs, source image entry count and plain description
variantCount, minPrice, maxPrice, anyAvailablenumber, booleanAggregates used instead of the variant fields in product mode
changeType, previousPrice, previousAvailablestring, number, booleanOnly with monitorChanges: what changed and the previous snapshot values
variants, options, onSale, fullyOutOfStock, imageAltTextsarray, booleanProduct mode: every variant (price, list price, SKU, barcode, stock state, options, weight, image) nested in the same row, option names/values, sale and stock flags, image alt texts. One product = one charged row.
dataSource, ratingValue, reviewCountstring, numberproducts_json or jsonld (headless fallback); rating and review count when the product page publishes them (jsonld rows)
error, errorCode, scrapedAtstringerror and errorCode are null on result rows; failures use diagnostic records with both set

How to use Shopify Store Products Scraper

  1. Paste one or more store URLs into Store URLs, e.g. https://www.allbirds.com. A bare domain like gymshark.com also works.
  2. Leave One row per variant on for a price/stock feed keyed by SKU, or turn it off for one row per product with minPrice/maxPrice/anyAvailable.
  3. Optionally add Collection handles (e.g. mens-shoes) to scrape only those collections instead of the whole catalog.
  4. Click Start, then export the dataset as JSON, CSV, Excel or HTML.

Example input

{
"stores": ["https://www.allbirds.com"],
"maxProductsPerStore": 100,
"includeVariants": true,
"onlyAvailable": false
}

Example output

{
"store": "https://www.allbirds.com",
"productId": 7218356060240,
"productHandle": "mens-strider-explore",
"productUrl": "https://www.allbirds.com/products/mens-strider-explore",
"title": "Men's Strider Explore - Natural Black (Dark Grey Sole)",
"vendor": "Allbirds",
"productType": "Shoes",
"variantId": 41334293921872,
"variantTitle": "8.5",
"sku": "A11768M085",
"price": 130,
"compareAtPrice": null,
"discountPercent": null,
"available": true,
"option1": "8.5",
"imageUrl": "https://cdn.shopify.com/s/files/1/1104/4168/products/mens-strider-explore.jpg",
"imageCount": 3,
"scrapedAt": "2026-09-12T18:10:00.000Z"
}

Input parameters

ParameterTypeDefaultDescription
storesarray["https://www.allbirds.com"]Store URLs to scrape, one or many
maxProductsPerStoreinteger100Cap on products fetched per store (1-20000)
collectionHandlesarray[]Scope to specific collections instead of the whole catalog
includeVariantsbooleanfalseOne row per variant (true, flat CSV) or one row per product with all variants nested (false, one charge per product)
onlyAvailablebooleanfalseDrop out-of-stock variants/products
monitorChangesbooleanfalseOutput only changes since the previous run of the same scope
trackingIdstringnoneOptional name for a separate change snapshot
includeRemovedbooleantrueOutput removed rows when monitoring changes
structuredDataFallbackbooleantrueIf products.json is blocked/missing, read sitemap + product-page structured data instead of failing the store
includeInventorybooleantrueRead each product's public JSON for barcodes and real stock counts (one extra request per product, no extra charge)
maxConcurrencyinteger3Stores scraped in parallel (1-10); pages within a store are sequential

Pricing

Shopify Store Products Scraper uses pay-per-event pricing: $0.001 per result row, i.e. $1 per 1,000 rows, plus a negligible actor-start fee. Check the live Pricing tab for current tiers and start fees. The run reserves its remaining result budget before requesting each store and limits products and output rows accordingly. Barcodes and stock counts are included in the same row price; there is no enrichment add-on. Variant mode can produce multiple rows per product. Budget-limited exports are not complete catalog snapshots. Errors and invalid product rows are stored as diagnostics, not billable result rows; start fees may still apply.

Shopify Store Products Scraper vs. browser-based scrapers

Most "universal" e-commerce scrapers render every product page in a headless browser to scrape prices off the DOM — slow, and easily broken by a theme update. Shopify Store Products Scraper reads the same structured JSON the store's own theme fetches, so extraction does not depend on the storefront CSS selectors. The feed can still be blocked, disabled or changed by a store. It cannot see private apps, wholesale-only catalogs or draft products that are not on the public storefront, which is by design: it only reads what the merchant already publishes.

Run it from code

JavaScript (npm install apify-client):

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('webdatatools/shopify-products-scraper').call({
stores: ['https://www.allbirds.com'],
maxProductsPerStore: 50,
includeVariants: false,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].title, items[0].variants.length, items[0].inventoryAccuracy);

Python (pip install apify-client):

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('webdatatools/shopify-products-scraper').call(
run_input={'stores': ['https://www.allbirds.com'], 'maxProductsPerStore': 50, 'includeVariants': False},
)
for item in client.dataset(run['defaultDatasetId']).iterate_items():
print(item['title'], item['minPrice'], item['currency'])

CLI (npm install -g apify-cli, then apify login):

$echo '{"stores":["https://www.allbirds.com"],"maxProductsPerStore":20}' | apify call webdatatools/shopify-products-scraper --output-dataset

Using Shopify Store Products Scraper with AI agents and MCP

Connect your MCP client to the official Apify MCP server, authorize your Apify account, and select webdatatools/shopify-products-scraper. Ask the agent to export two products from a public Shopify store, pass stores and maxProductsPerStore: 2, and inspect both the dataset and the OUTPUT completeness summary. Configure your run spending limit in Apify before expanding the export.

This Actor uses limited permissions. Full-permission Actors are excluded from the official MCP server; pay-per-event pricing is not a universal requirement for account-authorized MCP access. Per-request agentic payments have separate eligibility requirements. The same Actor is callable through Apify's REST API and integrations such as n8n or Make.

Shopify run summary and completeness

Read the OUTPUT key-value record before replacing an existing inventory. It reports resultRows, failedStores, emptyStores, budgetSkippedStores, budgetLimitedStores, truncatedRows and deliveryUnconfirmed. With monitorChanges it also reports baselineStores, comparedStores, changeRows, stateNotSaved, monitorMessages and comparisonSkipped (store and reason). Counters cover this process attempt, not inferred migration totals. A nonzero limit/failure counter means the export must not be treated as a full catalog. The configured product limit and availability filters can also produce a partial catalog. Repeated pages stop with a diagnostic instead of looping. An uncertain dataset upload is not retried automatically to avoid duplicate charges.

Run outcome, status message and error codes

OUTPUT.outcome is the final result, and the run's terminal status message (also saved as OUTPUT.statusMessage) repeats it in one line, e.g. PARTIAL: 12 accepted rows; 1 diagnostics (STORE_PASSWORD_PROTECTED=1). See OUTPUT and DIAGNOSTIC-*.

outcomeRun statusWhen
succeededSucceededNo store or input entry failed. Includes a reachable store with 0 products: OUTPUT.zeroResultReason then says why (EMPTY_STORE_OR_FILTER, NO_CHANGES_OR_BASELINE, BUDGET_LIMIT_REACHED or NO_RESULTS).
partialSucceededUsable rows were delivered, but some stores or input entries failed.
failedFailedNo valid store URL, a processing or dataset-delivery failure, or no usable rows while a store or input entry failed.

Only accepted product/variant rows (resultRows) enter the default dataset and are charged. Every rejected item is one DIAGNOSTIC-* record (diagnosticRows, never charged) with a human-readable error and a stable errorCode; OUTPUT.diagnosticCodes counts them by code, and OUTPUT.invalidStoreInputs counts skipped store entries. Codes are never renamed.

errorCodeCounts as failed storeMeaning
INVALID_INPUT— (run fails)No valid store URL was supplied
INVALID_STORE_URL— (input entry)A store entry is not a valid domain and was skipped
STORE_FETCH_FAILEDyesproducts.json request failed (network error or timeout)
STORE_PASSWORD_PROTECTEDyesStore is password-protected
STORE_NOT_SHOPIFYyesNo public Shopify feed and no verifiable product pages via the fallback
STORE_FEED_HTTP_ERRORyesFeed answered with an HTTP error; completeness cannot be verified
INVALID_PRODUCT_IDENTITYyesFeed returned a product without a valid id
PAGINATION_STALLEDyesFeed repeated the same page
STRUCTURED_DATA_PARTIALyesSome fallback product pages failed; usable rows are kept
STORE_PROCESSING_ERRORyesUnexpected error while processing the store
STORE_FAILEDyesGeneric store failure without a more specific code
INVALID_PRODUCT_DATAyesA row without a valid product/variant id or title was rejected
EMPTY_STORE_OR_FILTERno (emptyStores)Reachable store with no products, or none matched the filters
DATASET_DELIVERY_UNCONFIRMED— (run fails)Dataset upload not confirmed and not retried
MONITOR_STATE_NOT_SAVED—Change-monitoring snapshot could not be saved
PROCESSING_FAILED— (run fails)Store processing stopped because of an internal error

FAQ

How much does a typical export cost? You pay per accepted result row: in product mode one row per product, with all its variants, barcodes and published stock counts nested in it. 1,000 products cost $1.00 on the free tier (less on paid Apify plans; see the Pricing tab), plus a start fee of a fraction of a cent. Variant mode bills one row per variant, so a store with 4 variants per product costs about 4x more for the same products. Diagnostics, blocked stores and filtered-out rows are never billed. Set Max total charge on the run to cap spend.

Does this work on any Shopify store? It reads the public products.json feed; if that feed is blocked or missing (headless storefronts), it uses the sitemap and product-page structured data instead. Those jsonld rows have no public Shopify variant ID, compare-at price or timestamps (null), and a store whose pages publish no Shopify product ID cannot be verified. Password protection or blocks on every route still prevent collection; failed product pages are counted as a partial-export diagnostic. These cases create DIAGNOSTIC-* key-value records, with counts and the first diagnostic key in OUTPUT. Source failures with no usable products fail the run. A valid empty feed or no filter matches is reported in emptyStores without a product charge. Mixed runs retain usable rows and report failed stores separately. Error rows are not written to the default dataset.

Do I get real stock counts? Yes, where the store publishes them. The catalog feed only says in stock or not, so with includeInventory on (default) the Actor also reads each product's public JSON and fills inventoryQuantity, barcode, inventoryManagement and inventoryPolicy per variant. Many stores publish counts; some hide them — those rows say inventoryAccuracy: "not_published" and the count stays null, never zero. If the store blocks that endpoint, or the run is close to its timeout, enrichment stops and the affected rows say not_checked; the catalog rows are still delivered. OUTPUT.inventoryAccuracyRows counts each case. There is no extra charge for this.

Where does currency come from? One optional /meta.json request per nonempty store supplies the explicitly published store base currency. Missing, invalid or inaccessible metadata leaves it null and does not discard products. This request has an eight-second timeout and no retries. Shopify Markets may localize prices; the base currency is not proof of a visitor's presentment currency. Verify the target market before converting or comparing prices. The Actor never guesses from a domain or currency symbol.

Which images are included? imageUrls contains all distinct HTTP(S) URLs from the product's image list, plus its separate primary image when present. In variant mode, imageUrl prefers an explicit featured image, then the image linked by image ID or variant ID, then the primary product image. No extra image requests are made. imageCount keeps its existing meaning: the number of entries in the source image array, before URL validation and deduplication.

How is a product's discount calculated? Product mode reports the largest valid discount among its variants, pairing each variant's selling price with that same variant's compare-at price. It never combines the cheapest variant with another variant's list price. No valid discounted variant means null.

What counts as a "row" for pricing? Each variant (or, in product mode, each product) is one row. A store with 250 products and 4 variants each produces about 1,000 rows.

Does this access private Shopify data? No. It uses the storefront's public product feed and does not authenticate to Shopify Admin or bypass a password-protected store. Check the store's terms and your intended use before collecting data.

Can I export to CSV or Excel? Yes, from the Output tab or the API, with a ready-made Overview view.

Part of the webdatatools web-intelligence suite — every Actor is pay-per-event, reads public data without a login, and returns one clean row per entity:

Browse the whole suite at webdatatools, or call ten of these Actors straight from Claude, Cursor or Cline with the webdatatools MCP server.

Website & domain intelligence

Content for AI, LLMs and RAG

Search, video and social

Leads, jobs and company data

Developer, app and research data

Tracking a few competitor products rather than whole catalogs? Page & Price Watch watches a product page's price and availability from its schema.org data and emails you when they change. To see the price a single product page publishes right now, use the free product price checker (no sign-up).

Support and feedback

Found a store the Actor mis-detects as non-Shopify, or a field worth adding? Open an issue on the Issues tab.

If this Actor is useful to you, you can leave a review on its Apify Store page. Nothing is offered in return.