You want the product catalog of a WooCommerce store in a spreadsheet: a competitor whose prices you track, a supplier whose SKUs you need, or your own store before a migration. Clicking through category pages and copying prices does not scale, and you do not have admin access or API keys for a store you do not own.
Most WooCommerce stores already expose a public, read only Store API at /wp-json/wc/store/v1/products. It is the same API WooCommerce's own cart and checkout blocks use in the browser, and it has been on by default since WooCommerce 4. The WooCommerce Scraper: Store Products, Prices and Stock, published by Hay Equipos on the Apify Store, reads that API for any store address you give it and returns one clean row per product. No login, no API keys, no browser.
What you get back
One row per product. Illustrative example:
{
"store": "example-store.com",
"productId": 1042,
"name": "Classic Cotton T Shirt",
"slug": "classic-cotton-t-shirt",
"url": "https://example-store.com/product/classic-cotton-t-shirt/",
"type": "variable",
"sku": "TS-CLASSIC",
"price": 19.99,
"regularPrice": 24.99,
"salePrice": 19.99,
"onSale": true,
"currency": "USD",
"minPrice": 19.99,
"maxPrice": 22.99,
"inStock": true,
"stockStatus": "in-stock",
"rating": 4.6,
"reviewCount": 31,
"categories": ["Clothing", "T Shirts"],
"tags": ["cotton"],
"brands": [],
"imageUrl": "https://example-store.com/wp-content/uploads/tshirt.jpg",
"attributes": [{ "name": "Size", "values": ["S", "M", "L", "XL"] }],
"variationCount": 4,
"shortDescription": "Soft everyday tee.",
"scrapedAt": "2026-09-27T07:28:52.693Z"
}
| name | sku | price | regularPrice | onSale | inStock | categories |
|---|---|---|---|---|---|---|
| Classic Cotton T Shirt | TS-CLASSIC |
19.99 | 24.99 | true | true | Clothing, T Shirts |
| Canvas Tote Bag | BAG-TOTE |
15.00 | 15.00 | false | false | Accessories |
Prices are converted from the API's minor units to normal amounts (19.99, not 1999), and zero decimal currencies stay whole. For variable products, price is the lowest variant price and minPrice and maxPrice give the range. Rows also carry all image URLs, stock text, low stock count, backorder flag, weight and dimensions when the store provides them. A RUN_SUMMARY record in the run's key value store lists products found and saved per store.
Step by step in the Apify Console
- Open the actor on the Apify Store (link at the end) and click Try for free.
- In Store URLs, add store home pages, one per line (
https://example-store.comor justexample-store.com). A product or shop link from the store also works. The actor tries both URL styles the Store API can use, with and withoutwww.. - Set Maximum products per store (default 1,000) and Maximum products in total (default 20,000).
- Optional filters: Keyword filter (uses the store's own product search), Only products on sale, Only products in stock.
- Pick Sort by: store default, newest, price low to high or high to low, best selling, top rated, or name. "Newest" with a small maximum is a cheap way to watch for new products.
- Turn on Include full description if you want the long description as plain text. The short description is always included.
- Leave Respect robots.txt on. Click Start and export from the Output tab as CSV, Excel or JSON.
Calling it from code
The actor id is pistachio_implementation/woocommerce-store-products. Keep your token in APIFY_TOKEN.
curl:
curl -X POST \
"https://api.apify.com/v2/acts/pistachio_implementation~woocommerce-store-products/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"storeUrls": ["https://porterandyork.com"], "maxProductsPerStore": 100}'
Python with apify-client, a daily price check:
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("pistachio_implementation/woocommerce-store-products").call(
run_input={
"storeUrls": ["https://porterandyork.com", "https://www.nutribullet.com"],
"maxProductsPerStore": 500,
"onSaleOnly": True,
}
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if "error" in row:
print("store skipped:", row["store"], row["error"])
continue
print(row["store"], row["sku"], row["name"], row["price"], row["currency"])
Store the results each day and compare price, onSale and inStock by productId to see what changed.
Pricing
Pay per event, with no subscription, no start fee and no platform usage charge on top:
- $0.0008 per product saved ($0.80 per 1,000 products).
- Store level error rows are free: not a WooCommerce store, API turned off, request blocked, or robots.txt refusal.
For example, a full export of a 2,000 product store costs $1.60.
Limits and what it does not do
- WooCommerce only. It works on WooCommerce stores (version 4 or later) that leave the public Store API on, which is the default. A store built on another platform, or one that turned the API off, comes back as one free error row.
- Stores behind strong bot protection may block traffic from cloud servers. The actor does not try to get around blocks.
-
Variations are counted, not listed.
variationCounttells you how many variants a product has, and the price range covers them, but each variant is not its own row. - Prices are what the store shows a guest visitor from a US datacenter. Stores that change prices by country or customer group may show you different prices.
- No store discovery. You need to supply the store addresses.
- It makes one request per second per store, at 100 products per request, so very large catalogs take a few minutes.
This is an independent tool and is not affiliated with WooCommerce or Automattic. Please use the data you collect in line with each store's terms.
Try it on the Apify Store: https://apify.com/pistachio_implementation/woocommerce-store-products
Top comments (0)