Shopify Store Scraper: Products, Prices & Stock avatar

Shopify Store Scraper: Products, Prices & Stock

Pricing

from $1.00 / 1,000 product scrapeds

Go to Apify Store
Shopify Store Scraper: Products, Prices & Stock

Shopify Store Scraper: Products, Prices & Stock

Scrape any Shopify store's full catalog from public products.json: prices, compare-at, variants, SKUs, stock and inventory. Track new, removed and price changes between runs. WooCommerce too. $1 per 1,000 products.

Pricing

from $1.00 / 1,000 product scrapeds

Rating

0.0

(0)

Developer

Majd Hijazin

Majd Hijazin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

Shopify Store Scraper — products, prices, variants & stock

Shopify Store Scraper exports the full product catalog of any Shopify store to JSON, CSV or Excel. It reads the public products.json endpoint that every Shopify store exposes, so it needs no login, browser or proxy. You get prices, compare-at prices, every variant and SKU, stock availability and (where the store exposes it) inventory quantity.

Turn on change tracking and schedule the scraper. Each run then reports which products are new, removed, price changed or availability changed since the last run. That makes it a cheap Shopify price and stock monitor for competitor research, MAP monitoring, dropshipping and AI agents.

  • 🛍️ Any Shopify store: paste a domain like allbirds.com, a collection URL or product URLs.
  • 💲 Prices, discounts, variants and SKUs: priceMin, priceMax, compareAtPrice, onSale and discountPercent, plus a variants[] array with SKU, options, price, availability, barcode and weight.
  • 📦 Stock and inventory: available, available-variant counts, and per-variant inventory_quantity when the store exposes it.
  • 🔁 Change tracking: new, removed, price_changed and availability_changed, including per-variant price and stock deltas.
  • 🏪 Optional store profile: theme, currency, country, contact page, emails, social links, detected apps (Klaviyo, Judge.me, Recharge, Gorgias…) and the product count.
  • 🧩 WooCommerce too: if a URL is not Shopify, the scraper tries the public WooCommerce Store API (/wp-json/wc/store/v1/products).
  • 💵 $1 per 1,000 products ($0.001 each). No start fee, and platform usage is included.

What data can you extract from a Shopify store?

FieldExampleNotes
store, storeUrlkith.comCanonical host after redirects
productId, handle, title, url8286105305216, bb-139855The ID is a string, so no 64-bit precision loss
vendor, productType, tags[]BAUBLEBAR, Toys & Collectibles
priceMin, priceMax, currency35, 35, USDCurrency comes from /meta.json or /cart.js
compareAtPrice, onSale, discountPercent50, true, 50The highest compare-at price across variants
available, variantCount, availableVariantCounttrue, 7, 5
totalInventory, variants[].inventoryQuantity172Needs includeInventory, or product URLs. Null when the store hides stock
variants[]id, sku, title, option1-3, price, compareAtPrice, available, barcode, gramsCan be turned off with includeVariants: false
options[], images[], featuredImageSize: S/M/L
createdAt, updatedAt, publishedAtISO 8601From Shopify
changeType, changes[], variantChanges[], previousPriceMin, previousAvailableprice_changedOnly when trackChanges is on
descriptionplain textOpt-in with includeDescription
scrapedAtISO 8601

The dataset uses one flat record per product. Store profiles (recordType: "store") and error records (recordType: "error") are in the same dataset. Use the Products, Changes since last run, Store profiles and Errors views to see each type.

How to scrape a Shopify store

  1. Paste one or more store URLs into Store URLs, e.g. https://www.allbirds.com.
  2. Optionally restrict the scrape with Collection URLs (/collections/mens) or Product URLs (/products/tree-runner).
  3. Set Max products per store, then click Start.
  4. Download the results as JSON, CSV, Excel or HTML, or read them through the API.

To build a Shopify price monitor, enable Track changes since the last run and Output only changed products. Then create a schedule (for example daily). The first run records a baseline. Later runs output and bill only the products that changed.

Input example

{
"storeUrls": ["https://www.allbirds.com", "https://kith.com"],
"collectionUrls": ["https://www.brooklinen.com/collections/sheets"],
"productUrls": ["https://www.allbirds.com/products/mens-tree-runners"],
"maxProductsPerStore": 500,
"includeVariants": true,
"includeInventory": false,
"trackChanges": true,
"onlyChanges": false,
"analyzeStore": true
}
OptionDefaultWhat it does
storeUrls—Store home pages. The whole catalog is scraped via /products.json?limit=250&page=N
collectionUrls—Uses /collections/<handle>/products.json
productUrls—Uses /products/<handle>.js (includes inventory when it is exposed)
maxProductsPerStore1000Hard cap per store, from 1 to 100,000
includeVariantstrueAdds the variants[] array
includeInventoryfalseMakes one extra .js request per product to read inventory_quantity. Slower, same price
includeDescriptionfalseAdds the plain-text description
trackChanges / onlyChangesfalseDiffs against the previous run, which is stored in the named key-value store stateStoreName
analyzeStorefalseAdds one store profile per store (+$0.005)
includeWooCommercetrueFalls back to the WooCommerce Store API
requestDelayMs500Polite delay between requests to the same store
maxConcurrency3Number of different stores scraped in parallel

Output example

{
"recordType": "product",
"platform": "shopify",
"store": "kith.com",
"productId": "8286105305216",
"handle": "bb-139855",
"title": "BAUBLEBAR Kids DIY Bag Charm Craft Kit - Multi",
"vendor": "BAUBLEBAR",
"productType": "Toys & Collectibles",
"tags": ["accessories"],
"priceMin": 35,
"priceMax": 35,
"compareAtPrice": null,
"onSale": false,
"currency": "USD",
"available": true,
"variantCount": 1,
"availableVariantCount": 1,
"totalInventory": null,
"sku": "10866430",
"variants": [
{ "id": "45137916461184", "sku": "10866430", "title": "OS", "price": 35, "compareAtPrice": null, "available": true, "inventoryQuantity": null, "grams": 1361 }
],
"images": ["https://cdn.shopify.com/s/files/1/0094/2252/files/139855_G_01_SQ.jpg"],
"url": "https://kith.com/products/bb-139855",
"createdAt": "2026-01-27T18:20:26-05:00",
"updatedAt": "2026-10-20T14:19:33-04:00",
"publishedAt": "2026-10-20T10:57:22-04:00",
"source": "products.json",
"changeType": "new",
"scrapedAt": "2026-10-20T18:19:32.058Z"
}

Store profile (analyzeStore: true):

{
"recordType": "store",
"store": "www.allbirds.com",
"storeName": "Allbirds",
"currency": "USD",
"country": "US",
"myshopifyDomain": "weareallbirds.myshopify.com",
"theme": { "name": "[DNAM Theme July 2026]", "schemaName": "allbirds-theme", "schemaVersion": "1.231.12", "themeStoreId": null },
"contactPage": null,
"emails": ["help@allbirds.com"],
"socialLinks": { "instagram": "https://www.instagram.com/allbirds", "tiktok": "https://www.tiktok.com/@weareallbirds" },
"detectedApps": ["Attentive", "Google Tag Manager", "Shop Pay", "Elevar"],
"productCount": 692,
"collectionCount": 1346
}

A per-store run summary (status, counts, change totals, billing and HTTP stats) is saved as the RUN_SUMMARY record in the default key-value store.

Pricing

This Actor uses pay-per-event pricing, and Apify platform usage is included.

EventPriceWhen it is charged
product-scraped$0.001 ($1 per 1,000)For each product record delivered
store-analyzed$0.005Once per store, only with analyzeStore: true and only when a profile was built

You are not charged for error records, removed records, or (with onlyChanges) unchanged products. Examples: 1,000 products cost $1.00. A daily monitor of a 2,000-product store where about 50 products change per day costs about $0.05 per day after the first run.

The run respects your maximum charge per run (maxTotalChargeUsd). The scraper stops delivering once the budget is used up, and it never bills a product it did not deliver. If the budget cannot cover a single product, the run stops immediately with BUDGET_TOO_LOW.

API

Run the scraper and fetch the products in one call with the Apify API:

curl -X POST "https://api.apify.com/v2/acts/impressionable_lupine~shopify-store-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"storeUrls":["https://www.allbirds.com"],"maxProductsPerStore":100}'

JavaScript (apify-client):

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('impressionable_lupine/shopify-store-scraper').call({
storeUrls: ['https://kith.com'],
maxProductsPerStore: 500,
trackChanges: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems({ view: 'overview' });
console.log(items.filter(i => i.changeType === 'price_changed'));

Python (apify-client):

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("impressionable_lupine/shopify-store-scraper").call(run_input={"storeUrls": ["https://www.brooklinen.com"], "maxProductsPerStore": 200})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["priceMin"], item["currency"], item["available"])

MCP (Claude, Cursor and other AI agents)

AI agents can call this scraper as a tool through the Apify MCP server. Add it to your MCP client:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?actors=impressionable_lupine/shopify-store-scraper",
"headers": { "Authorization": "Bearer <APIFY_TOKEN>" }
}
}
}

Then ask, for example: "Get the 50 newest products from allbirds.com with prices and stock", or "Which kith.com products changed price since the last run?". The flat output and the changeType field let an agent answer without extra parsing.

Integrations

Send the results to Google Sheets, Airtable, Slack, Zapier, Make, n8n or a webhook with Apify integrations. For example, post every price_changed product to Slack after each scheduled run.

Limitations

  • Public catalog only. The scraper reads the same data as a store's visitors. It cannot see unpublished products, B2B or wholesale price lists, customer-specific pricing or cost of goods.
  • Inventory quantity is optional per store. Many stores hide inventory_quantity (then totalInventory is null), and some cap or round it. Availability (available) is always present.
  • Prices are in the store's default currency and market. Shopify Markets prices for other countries are not fetched.
  • Some stores block public catalog endpoints. Headless storefronts (Hydrogen, Next.js) may block products.json on their public domain. The scraper then automatically tries the www/apex twin host and the store's *.myshopify.com domain. If all of these are blocked, you get a PRODUCTS_JSON_DISABLED error record. Sites behind a JavaScript bot challenge return BOT_CHALLENGE. Password-protected stores return STORE_PASSWORD_PROTECTED. No proxies or CAPTCHA solving are used.
  • WooCommerce support uses the public Store API. It gives prices, stock status, images and categories, but no variant-level prices and no created/updated dates. Collection and product URL scoping is Shopify-only.
  • Removed-product detection needs a complete crawl. If a run is capped by maxProductsPerStore or by your budget, no product is reported as removed.
  • Very large catalogs are paginated 250 products at a time with a polite delay. A 10,000-product store takes about 40 requests, which is roughly 20–30 seconds.

FAQ

Is it legal to scrape Shopify stores? The scraper only reads public, unauthenticated catalog data that the store itself publishes as JSON, at a polite rate. You are responsible for how you use the data. Do not republish copyrighted descriptions or images without permission, and check the store's terms and your local law.

Do I need a Shopify API key or the store owner's permission to access products.json? No. products.json is a public endpoint on every Shopify storefront. The Shopify Admin API (with inventory levels, orders and costs) is only available to the store owner and is not used.

How do I know whether a site is a Shopify store? Just paste it. The scraper detects Shopify automatically and returns a NOT_SHOPIFY error record (free) for other sites. With includeWooCommerce on, it also scrapes WooCommerce stores.

How do I monitor competitor prices on Shopify? Enable trackChanges and onlyChanges, then schedule the Actor. Each run outputs only new, removed, price-changed and availability-changed products, with the previous price and stock.

Can I get inventory or stock levels? Yes, where the store exposes them. Enable includeInventory (or pass product URLs) to read inventory_quantity per variant from /products/<handle>.js. available and availableVariantCount are always included.

How many products can I scrape? Up to 100,000 per store per run. Use maxProductsPerStore to keep test runs small and cheap.

Why do I see records with recordType: "error"? Each input URL that could not be scraped (not Shopify, password page, missing collection or product, rate limit) produces one free error record. This way you always know why a store returned no products. The run itself still succeeds.

Does it use proxies or a browser? No. It makes plain HTTPS JSON requests, one at a time per store, and backs off on HTTP 429 using Retry-After. That keeps it fast, cheap and reliable.