Shopify & WooCommerce Scraper and Price Tracker
Pricing
from $0.72 / 1,000 products
Shopify & WooCommerce Scraper and Price Tracker
Shopify scraper and WooCommerce scraper in one run: every product with prices, variants, SKUs, barcodes, stock and images, to JSON, CSV or Excel. Schedule it as a price tracker that outputs only what changed since the last run (price drops, restocks, new and removed products), charged per change.
Pricing
from $0.72 / 1,000 products
Rating
0.0
(0)
Developer
AgentToolWorks
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
15 days ago
Last modified
Categories
Features
Share
What is this Shopify and WooCommerce scraper?
This Actor is a Shopify scraper and WooCommerce scraper in one, with a built-in price tracker. Paste store URLs from both platforms and get one row per product in one format: prices, variants, SKUs, barcodes (GTIN/UPC/EAN), stock and images.
In short
- What you get: a whole catalog, a collection or only the product URLs you list; with change tracking on, only what changed since the last run (price drops, restocks, new and removed products).
- What it costs: $0.90 per 1,000 products on Apify's Free plan, against $1.00 at autofacts and trovevault, the two most used Shopify product scrapers (Business plans: $0.72 here, $0.70 at autofacts); $0.005 per change.
- Why this one: neither leader outputs changes (their READMEs: compare the runs yourself), so checking a 2,000-product store daily for 30 days costs $1.45 here against about $60 with either of them (60,000 products re-read), at 10 variant changes a day. Measured on Apify: Shopify and WooCommerce in one run; headless stores such as Gymshark and Bombas read through their Shopify Online Store host, no proxy (2026-10-17).
Prices and READMEs read on the Apify Store on 2026-10-26; each cost is worked out under How it compares.

What data can you extract from Shopify and WooCommerce stores?
One row per product, with its variants nested (or one row per variant, see Output). Every row has the same fields whichever platform the store runs on.
| Product data | Variant data | Store data |
|---|---|---|
| Title, handle, URL | Variant title and options (Size, Color) | Platform detected (Shopify or WooCommerce) |
| Vendor or brand, product type | SKU and barcode (GTIN, UPC, EAN) | Store domain and URL |
| Categories and tags | Price and compare-at (sale) price | Currency |
| Lowest and highest price, compare-at price, on sale | In stock, stock quantity where public | Collection or category read |
| Options with all their values | Backorder policy | Run summary per store |
| Images: URL, width, height, alt text, linked variants | Weight in grams | |
| Description as plain text and as HTML | Requires shipping, taxable | |
| Rating and review count (WooCommerce) | Variant image and position | |
| Created, updated and published dates | Created and updated dates (Shopify) |
Everything comes from the store's own public storefront: Shopify's products.json and Ajax product endpoints, and the public WooCommerce Store API. No login, no account, no customer data.
Headless Shopify stores work too. Many big brands run a custom storefront (Hydrogen, Next.js) in front of Shopify, or put a bot wall on their main domain, so their own address does not serve products.json. Their Shopify Online Store still does, on another public host: the myshopify.com domain their home page names, or shop. on the same domain. The Actor finds it, checks that its /meta.json names the brand's domain before reading anything, and says in the run summary which host it read and why. Measured on 2026-10-17: Gymshark, Fashion Nova, Ruggable, Bombas, SKIMS, Chubbies, Dr. Squatch, Vuori and Rhone, all read this way, with the same fields as any other Shopify store.
What can you use Shopify and WooCommerce product data for?
- Competitor price monitoring: track price changes and discounts across many stores, daily or hourly.
- Restock alerts: know when a sold-out product or size is back in stock.
- Product research and dropshipping: see what a brand sells, at what price, and what it launches.
- Price comparison and product feeds: match products across stores by barcode (GTIN) and build a feed.
- Catalog audits: missing SKUs or barcodes, out-of-stock share, sale depth, image alt text coverage.
- Data for AI agents and pipelines: one predictable JSON schema for any Shopify or WooCommerce store.
How to scrape a Shopify or WooCommerce store
- Click Try for free (or Start) on this page and sign in to Apify.
- Paste one or more store URLs in Store URLs: a home page (
https://www.allbirds.com), a Shopify collection (https://www.allbirds.com/collections/mens), a WooCommerce category page, or a product page to read only that product. - Set Max products per store. You pay per product returned, so start small to look at the data.
- Turn on Include stock levels, barcodes and image alt text if you need them (Shopify; slower).
- Click Start. When the run finishes, open the Output tab, pick the Products or Variants view, and export as JSON, CSV, Excel or XML, or read it through the API.

Input
| Field | What it does |
|---|---|
storeUrls | Shopify or WooCommerce store URLs, mixed. A home page, a Shopify collection page, a WooCommerce category page, or a product page (that product only, charged as one product). Up to 100 URLs per run. |
maxProductsPerStore | Stop after this many products per store (default 1,000, up to 100,000). You pay per product returned. |
trackChanges | Save a snapshot per store and output what changed since the last run. |
outputProducts | Turn off to output only the changes (for scheduled monitoring). |
collection | A Shopify collection handle or WooCommerce category slug, for every store whose URL does not already name one. |
includeInventory | Shopify: read each product's public Ajax endpoint for stock counts, barcodes, backorder policy and image alt text. Slower: at most 500 products per store per run. |
includeDescription | Add description (plain text) and descriptionHtml (the store's HTML), plus shortDescription on WooCommerce. |
proxy | none (default) or datacenter (Apify Proxy) if a store rate-limits your runs. |
Example: one Shopify collection and one WooCommerce store, with barcodes and stock.
{"storeUrls": ["https://www.allbirds.com/collections/mens", "https://woocommerce.com"],"maxProductsPerStore": 500,"includeInventory": true}
Example: a daily price and stock monitor that outputs only changes.
{"storeUrls": ["https://competitor-one.com", "https://competitor-two.com"],"maxProductsPerStore": 20000,"trackChanges": true,"outputProducts": false}
Output
The dataset has three ready-made views in Apify Console and in the API:
- Products: one row per product.
- Variants (one row each): one row per variant, with SKU, barcode, price, stock and weight. The same as adding
unwind=variantsto the dataset API. - Changes: the change rows, when change tracking is on.


A Shopify product row from a real run (variants and images shortened):
{"rowType": "product","platform": "shopify","storeDomain": "www.allbirds.com","storeUrl": "https://www.allbirds.com","productId": "7340901859408","handle": "womens-allbirds-flip-flop-dusty-pink","title": "Women's Allbirds Flip Flop - Dusty Pink","url": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink","vendor": "Allbirds","productType": "Shoes","categories": [],"tags": ["allbirds::complete => true", "allbirds::edition => limited"],"price": 25,"priceMax": 25,"compareAtPrice": 50,"onSale": true,"currency": "USD","available": true,"requiresShipping": true,"sku": null,"inventoryQuantity": 13,"variantCount": 7,"options": [{ "name": "Size", "position": 1, "values": ["5", "6", "7", "8", "9", "10", "11"] }],"variants": [{"variantId": "42146889039952","variantTitle": "5","variantSku": "A12513W050","variantOptions": { "Size": "5" },"variantPrice": 25,"variantCompareAtPrice": 50,"variantAvailable": false,"variantInventory": 0,"variantAllowsBackorder": false,"variantBarcode": "196942293089","variantWeightGrams": 455,"variantRequiresShipping": true,"variantTaxable": true,"variantImage": null,"variantPosition": 1,"variantCreatedAt": "2026-03-27T14:03:50-07:00","variantUpdatedAt": "2026-09-30T17:03:07-07:00"}],"image": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345","images": ["https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345"],"imageDetails": [{"src": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345","alt": null,"width": 4000,"height": 4000,"position": 1,"variantIds": []}],"imageCount": 5,"rating": null,"reviewCount": null,"createdAt": "2026-03-27T14:03:50-07:00","updatedAt": "2026-09-30T17:03:07-07:00","publishedAt": "2026-09-25T16:58:13-07:00","collection": null,"scrapedAt": "2026-10-01T00:03:06.433Z"}
A WooCommerce product row has exactly the same fields. Where the WooCommerce Store API publishes nothing, the field is null rather than guessed: no creation or update dates, no barcodes, no shipping or tax flags. In exchange, WooCommerce rows carry rating and reviewCount.
{"rowType": "product","platform": "woocommerce","storeDomain": "woocommerce.com","productId": "18734006912442","title": "PH PostNord Shipping with Print Label","url": "https://woocommerce.com/products/ph-postnord-shipping-with-print-label/","categories": ["WooCommerce extensions", "Shipping, delivery and fulfillment", "Label printing"],"price": 99,"currency": "USD","available": true,"variantCount": 1,"imageCount": 5,"rating": null,"reviewCount": 0}
Prices are numbers in the row's currency (25 means 25.00), so they sort and chart directly. On Shopify they are always in the shop's own currency, from its /meta.json: stores using Shopify Markets would otherwise convert them to the currency of whatever country the request comes from, under the same label. Weights are always grams, whatever unit the store uses.
Change rows
With trackChanges on, each run also outputs one row per change since the previous run. A real one: two sizes of a sneaker came back in stock at Kith between two runs 46 minutes apart.
{"rowType": "change","changeType": "back_in_stock","platform": "shopify","storeDomain": "kith.com","storeUrl": "https://kith.com","productId": "8286820892800","variantId": "45144949096576","title": "adidas x Entire Studios Adizero Adios 9 - Core Black / Feather Grey / Mist Stone","variantTitle": "7","handle": "aakj2303","url": "https://kith.com/products/aakj2303","price": 180,"oldPrice": null,"newPrice": null,"available": true,"currency": "USD","detectedAt": "2026-10-01T01:05:34.945Z","previousCheckAt": "2026-10-01T00:19:56.772Z"}
changeType is one of new_product, removed_product, price_up, price_down, back_in_stock, out_of_stock. Price changes fill oldPrice and newPrice (compared to the cent); stock changes leave them null and give the current price. A change on a single variant carries its variantId and variantTitle; a new or removed product has them null.
Run summary
The run's key-value store holds a SUMMARY record with the status of each store: ok, partial (stopped part way, with the reason; the products read are kept), blocked (the site refused the request, a bot challenge or an HTTP 403, and no Shopify Online Store host confirmed itself for it), unsupported (neither a Shopify store nor a WooCommerce store with its Store API open), refused (not a public https address) or failed (for example a collection that does not exist, with the store's real collections listed, or a product URL whose product the store does not have and was never tracked). It also says whether a store had more products than your limit, so a cut catalog is never silent. For a product URL, product names the product read (its handle or slug).
For Shopify stores it also gives route and catalogHost, where the catalog was read: products.json on the address you gave, or for a headless or walled storefront myshopify-domain, sibling-domain (such as shop.bombas.com) or label-myshopify, with a note saying why. storeName and storeCountry come from the store's /meta.json: a brand with several regional Shopify stores (Gymshark US is gymsharkusa.myshopify.com) shows which one was read.
How to monitor competitor prices with a schedule
- Run the Actor once with Track changes since the last run on. This first run saves a baseline for each store and reports no changes.
- On the input page, click Save as a new task. Turn off Output product rows in the task if you only want the changes.
- Open Schedules, create a schedule (daily, or every few hours) and add the task to it.
- Every scheduled run outputs only what changed since the previous run. Add an integration (email, Slack, Google Sheets, a webhook, Make, Zapier or n8n) to be notified when a run has rows.
Snapshots are kept in your account's named key-value store storecatalog-state, one record per store and per collection or category (snapshot-<domain>, snapshot-<domain>--<collection>), and one per product watched through its URL (product-<domain>--<productId>, keyed by the product's id so a renamed product keeps its history, with small handle-<domain>--<handle> records pointing to it). Delete a record to start that store or product over from a new baseline.
Two things worth knowing:
- Cover the whole catalog (
maxProductsPerStoreabove the store's size) to detect new and removed products. When a run stops at your limit, a product it did not reach may still exist, so the run reports only price and stock changes for that store and says so in the summary. - If a run stops at your spending limit before every change is delivered, that store's snapshot is not updated, so the next run reports those changes again instead of losing them.
How it compares
The two most used Shopify product scrapers on the Apify Store are trovevault/shopify-products-scraper (106 users in the last 30 days) and autofacts/shopify (91), per their Store records read on 2026-10-26 at 19:42 UTC. Prices below are each Actor's current entry in its public Store record (pricingInfos), and features are what its own README says, both read on 2026-10-26. We did not run these Actors.
Price per 1,000 products, by Apify plan (each plan has its own Store discount tier). The cheapest in each row is in bold:
| Apify plan (discount tier) | This Actor | autofacts/shopify | trovevault/shopify-products-scraper |
|---|---|---|---|
| Free | $0.90 | $1.00 | $1.00 |
| Starter (Bronze) | $0.90 | $0.90 | $0.95 |
| Scale (Silver) | $0.81 | $0.80 | $0.90 |
| Business (Gold) and above | $0.72 | $0.70 | $0.85 |
| Run start, per event | $0.00005 | $0.00005 | $0.001 on Free, down to $0.00085 |
| This Actor | autofacts/shopify | trovevault/shopify-products-scraper | |
|---|---|---|---|
| Changes since the last run | Built in, $0.005 per change; product rows can be turned off | None: its README says to "compare the runs" | None: its README says to "diff scheduled datasets" |
| WooCommerce stores | Same run, same row format, a price per variation; the store's Store API must be open | Separate Actor, autofacts/woocommerce-scraper, $1.20 per 1,000 on Free; also reads stores whose Store API is closed | Separate Actor, trovevault/woocommerce-products-scraper, $1.00 per 1,000; variable products at their lowest price only, per its README |
| A product URL | That product only, charged once | That product only | "any URL path is ignored": the whole store is read |
| Barcodes and stock counts | Included in the product price, up to 500 products per store per run | On store crawls, barcodes with Grab real inventory, plus $1.00 per 1,000 products that return a stock count | Plus $1.00 per 1,000 enriched products on Free |
| Headless or walled storefronts | Read from the store's own Shopify Online Store host once its /meta.json confirms it (Gymshark, Fashion Nova, Ruggable, Bombas, 2026-10-17, no proxy) | Its README recommends residential proxies against blocks | Its README lists headless stores as a possible limitation and suggests a residential proxy for brands such as Gymshark |
The same job on the Free plan. Each figure is the Actor's own Free-plan event prices times the events the job needs: products, the paid add-on where the Actor has one, and one start event per run (the minimum; an Actor charges one per GB of memory), rounded to the cent.
| Job, Free plan | This Actor | autofacts | trovevault |
|---|---|---|---|
| 1,000 products from one Shopify store | $0.90 | $1.00 | $1.00 |
| One Shopify and one WooCommerce store, 1,000 products each | $1.80, one run | $2.20, two Actors | $2.00 at 1 GB of memory, two Actors |
| 500 Shopify products with barcodes and stock counts | $0.45 | $0.50, or $1.00 where the store publishes stock counts | $1.00 |
| A 2,000-product store checked daily for 30 days, 10 changes a day | $1.45 | $60.00 | $60.03 |
The last row: both leaders output whole catalogs, so 30 daily checks re-read 60,000 products and finding what changed is left to you. This Actor, with Output product rows off, charges only the change rows: none on the first run, which saves the baseline, then 10 a day for 29 days (290 changes at $0.005), plus 30 run starts. Change-only monitoring stays cheaper than re-reading at $1.00 per 1,000 as long as a run finds fewer changes than a fifth of the products it checks (400 for a 2,000-product store).
A change is one variant's price or stock moving, or one product added or removed: a product whose six sizes all drop in price is six changes. A store-wide sale can therefore cost more in that one run than re-reading the catalog.
How much does it cost to scrape a Shopify store?
Pay per event, no subscription, platform usage included:
| Event | Price |
|---|---|
| Product returned | $0.90 per 1,000 products ($0.0009 each); $0.81 on Apify Scale plans, $0.72 on Business plans and above |
| Change detected | $0.005 per change |
| Run start | $0.00005 |
Examples:
- A 1,000 product store: $0.90.
- Apify's free plan includes $5 of monthly usage (apify.com/pricing), enough for about 5,500 products a month.
- A daily monitor of a 2,000 product store with Output product rows off pays only for changes: a day with 10 variant price or stock changes costs $0.05.
A run that finds nothing to output costs only the start fee. Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.
Use the Actor through the Apify API
Run it and get the products in one HTTP call (replace YOUR_APIFY_TOKEN, from Settings > API & Integrations in Apify Console):
curl -X POST "https://api.apify.com/v2/acts/agenttoolworks~shopify-woocommerce-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"storeUrls": ["https://www.allbirds.com"], "maxProductsPerStore": 100}'
Node.js, with the official apify-client:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('agenttoolworks/shopify-woocommerce-scraper').call({storeUrls: ['https://www.allbirds.com', 'https://woocommerce.com'],maxProductsPerStore: 100,includeInventory: true,});// One item per variant instead of per product: add { unwind: 'variants' }.const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.map((p) => [p.title, p.price, p.currency]));
Python, with the official apify-client:
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("agenttoolworks/shopify-woocommerce-scraper").call(run_input={"storeUrls": ["https://www.allbirds.com"], "maxProductsPerStore": 100})for product in client.dataset(run["defaultDatasetId"]).iterate_items():print(product["title"], product["price"], product["currency"])
The API tab on this page has ready-made snippets for every language and endpoint.
FAQ
Does it work on every Shopify store?
On nearly every one: products.json is part of every Shopify Online Store. When the address you paste does not serve it (a headless storefront, or a bot wall in front of the main domain), the Actor reads it on the store's Online Store host instead, once that host's /meta.json confirms it belongs to the same brand. In our 2026-10-17 tests, Gymshark, Fashion Nova, Ruggable and Bombas were read this way.
Which stores does it not work on?
Sites that refuse automated requests on every host and name no Shopify store we can confirm as theirs: in our 2026-10-17 tests, www.hellomolly.com (Vercel's bot checkpoint) and www.tesla.com (HTTP 403). Also WooCommerce stores whose Store API is closed or behind a firewall, and collection URLs that are not collections (kith.com/collections/mens redirects to an editorial page; the run summary lists the store's real collections instead). The run summary names every store that failed and why.
Does it work on every WooCommerce store?
On WooCommerce stores whose Store API is open, which is the default in recent WooCommerce versions. Stores that close it are reported as unsupported, and stores behind a firewall as blocked.
How fast is it?
Measured on Apify: Allbirds (692 products, 3 catalog requests) in 3 seconds, the first 5,000 Kith products in 29 seconds. A headless store costs a few requests more to find: 1,000 Gymshark products read in 5.5 seconds and 1,000 Bombas products in 7.4 seconds through their Online Store hosts (2026-10-17). With Include stock levels, barcodes and image alt text, each product needs one more request, so plan about 1.5 minutes per 300 products (measured: 300 Allbirds products in 90 seconds). WooCommerce speed depends mostly on the store's server.
Can I get stock quantities and barcodes?
Shopify: turn on Include stock levels, barcodes and image alt text. Barcodes come back for every product that has one; stock counts only for stores that track inventory publicly. WooCommerce publishes a stock count only when stock is low, and never barcodes.
Can I get one row per variant, for a spreadsheet?
Yes: choose the Variants (one row each) view before exporting, or add unwind=variants to the dataset items API.
Can I scrape only one collection or category?
Yes: paste the collection URL (/collections/sale) or the WooCommerce category URL, or set Collection or category to apply the same one to every store. The Actor checks the collection exists first: Shopify answers an empty list for a collection that does not exist, so a wrong handle is reported as not found, with the collections the store's own search finds, rather than as an empty catalog. A product URL reads only that product, even under a collection path (next question).
Can I scrape or track only some products?
Yes: paste each product's URL. It reads that product only and is charged as one product. Recognised forms:
- Shopify:
/products/<handle>, also under a collection (/collections/mens/products/<handle>) or a locale (/en-us/products/<handle>). - WooCommerce:
/product/<slug>/(the default),/products/<slug>/,?product=<slug>and?p=<id>. A store that chose its own product base, such as/shop/<category>/<slug>/, is recognised when its Store API gives that exact URL as the product's address; any other URL is read as the store.
Several product URLs of the same store in one run make a watch list (up to 100 URLs per run). With change tracking on, each product keeps its own snapshot, apart from the store's: only that product's price and stock changes are reported, and a whole-store monitor of the same store is not affected. On Shopify, a product URL row includes stock counts and barcodes where the store publishes them, without Include stock levels, barcodes and image alt text, because availability comes from the same per-product endpoint. Two URLs of the same product in one run (two ?variant= links, say) are output, tracked and charged once.
When a watched product disappears from the store, a run with change tracking on reports it once as a removed_product change (charged as one change, as a whole-store monitor charges the same removal) and the entry stays ok; later runs report nothing more until it is back, as a new_product. On WooCommerce, a product whose slug changed is read by its id first, so a new address is not reported as a removal. A product URL the store does not have and that was never tracked (a typo) is reported as failed in the run summary, and nothing is charged for it. Older WooCommerce versions cannot look a product up by its slug: the entry fails with that reason, and the ?p=<id> form of the URL still works there.
Can I use it with Make, Zapier, n8n or Google Sheets?
Yes, through Apify's integrations: the Actor's dataset works with every Apify integration, and a webhook can fire when a run finishes.
Can AI agents use it through MCP?
Yes. Apify's hosted MCP server exposes Store Actors to MCP clients such as Claude or Cursor: https://mcp.apify.com?tools=agenttoolworks/shopify-woocommerce-scraper.
Is it legal to scrape Shopify and WooCommerce stores?
The Actor reads only the product information a store publishes to every visitor, through the storefront's public endpoints, without logging in, and it collects no personal data. As with any data, how you use it is your responsibility.
Something is missing or broken?
Open an issue in the Issues tab: we read every one, and fields asked for by users get added.
If you found the Actor useful, please leave a short review on its Reviews tab so other users can judge it too.
Other Actors by AgentToolWorks
- AliExpress Scraper: Products, Prices and Shipping per Country: the supplier side, for stores that source on AliExpress: products by keyword, category or product URL, with every SKU's price and stock and the shipping cost to each country you list, plus a price tracker mode that returns only the changes.
- Facebook Ad Library Scraper: ads from the Meta, Google and Microsoft ad libraries in one table.
- LinkedIn Jobs Scraper and Indeed Scraper: public job listings of both boards in one row format.
- Job Scraper for Greenhouse, Lever, Ashby & Workable (JobsRadar): open roles from the public boards of seven applicant tracking systems, deduplicated into one list.
- Google Maps Scraper: every place inside a city's real boundary, with phone, website, rating, review count, opening hours and emails.
- Google Trends Scraper: interest over time, by region and related queries for terms and comparisons, plus each country's Trending now list.
Learn more
- What this Shopify and WooCommerce scraper returns, platform by platform
- How to scrape a Shopify store: products.json, the Ajax API and their traps
Built by AgentToolWorks. Not affiliated with Shopify or with WooCommerce.