E-commerce data

WooCommerce Products Catalog Scraper: prices, stock, categories

Product catalogue of any WooCommerce store from its public Store API, up to 200 stores a run and 5,000 products each: name, URL, SKU, price, sale price, currency, stock and on-sale flags, rating, categories, tags, brands, attributes, images and description. Robots-aware. Pay per product.

$1.00per 1,000 · Product listed

One product delivered. Stores that are not on WooCommerce, closed by robots.txt or without products are never charged.

Call it from your code

curl
ACTOR=steadydata~woocommerce-products-catalog
URL="https://api.apify.com/v2/acts/$ACTOR/run-sync-get-dataset-items"

curl -X POST "$URL?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"stores": ["barefootbuttons.com", "porterandyork.com"], "maxProductsPerStore": 500, "includeDescription": false}'
python
import os, requests

ACTOR = "steadydata~woocommerce-products-catalog"
URL = f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items"

rows = requests.post(
    URL,
    params={"token": os.environ["APIFY_TOKEN"]},
    json={'stores': ['barefootbuttons.com', 'porterandyork.com'], 'maxProductsPerStore': 500, 'includeDescription': False},
    timeout=900,
).json()

# every row carries a status; failures are records, not exceptions
ok = [r for r in rows if r.get("status") == "ok"]
print(len(ok), "rows delivered")
javascript
const ACTOR = "steadydata~woocommerce-products-catalog";
const url =
  `https://api.apify.com/v2/acts/${ACTOR}/run-sync-get-dataset-items` +
  `?token=${process.env.APIFY_TOKEN}`;

const rows = await fetch(url, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({"stores": ["barefootbuttons.com", "porterandyork.com"], "maxProductsPerStore": 500, "includeDescription": false}),
}).then((r) => r.json());

// one object per delivered row, same shape every time
console.log(rows.filter((r) => r.status === "ok").length, "rows");

Use it in an agent

Point an MCP client at Apify's hosted server with this dataset in the tools list, or run our own server locally. The field explanations in the table below travel with the schema, so your agent reads them straight from the platform instead of from this page.

mcp url
https://mcp.apify.com?tools=steadydata/woocommerce-products-catalog

# the tool arrives in your agent as steadydata--woocommerce-products-catalog

Input

FieldTypeNameWhat it does
stores
required
array of stringWooCommerce storesOne per row, up to 200: the store domain or any URL on it (barefootbuttons.com, https://porterandyork.com/shop/).
maxProductsPerStoreintegerMax products per storeCost ceiling per store, in the store's order. Pages of 100.
includeDescriptionbooleanInclude the product descriptionOn: short and full description as plain text. Off keeps rows small.
onlyInStockbooleanOnly products in stockOn: products that are out of stock are skipped and not charged.

What you get back

output schema, one object per delivered row
{
  "store": string | null,
  "productId": string | null,
  "name": string | null,
  "slug": string | null,
  "url": string | null,
  "sku": string | null,
  "type": string | null,
  "currency": string | null,
  "price": number | null,
  "regularPrice": number | null,
  "salePrice": number | null,
  "priceMin": number | null,
  "priceMax": number | null,
  "onSale": boolean | null,
  "inStock": boolean | null,
  "stockText": string | null,
  "lowStockRemaining": integer | null,
  "isPurchasable": boolean | null,
  "averageRating": number | null,
  "reviewCount": integer | null,
  "categories": array | null,
  "tags": array | null,
  "brands": array | null,
  "attributes": array | null,
  "variationCount": integer | null,
  "imageCount": integer | null,
  "imageUrl": string | null,
  "images": array | null,
  "weight": string | null,
  "shortDescription": string | null,
  "description": string | null,
  "storeProductTotal": integer | null,
  "status": string | null,
  "input": string | null,
  "errorCode": string | null,
  "error": string | null
}
FieldTypeWhen it is filled
storestring, null
productIdstring, null
namestring, null
slugstring, null
urlstring, null
skustring, nullFilled when the shop fills in an article number; it is an optional field in WooCommerce.
typestring, null
currencystring, null
pricenumber, null
regularPricenumber, null
salePricenumber, nullFilled when the product is on sale; regularPrice always holds the undiscounted price.
priceMinnumber, nullFor a product with variations the lowest variation price; for a simple product the same as price.
priceMaxnumber, nullFor a product with variations the highest variation price; for a simple product the same as price.
onSaleboolean, null
inStockboolean, null
stockTextstring, nullThe shop's own wording about availability, which differs per shop and per language.
lowStockRemaininginteger, nullFilled when the shop publishes a low-stock warning with a number, for example 'only 3 left'.
isPurchasableboolean, null
averageRatingnumber, nullFilled when the product has been reviewed; empty when reviewCount is 0.
reviewCountinteger, null
categoriesarray, null
tagsarray, nullFilled when the shop tags its products; many shops use categories only.
brandsarray, nullFilled when the shop uses a brands plugin; many shops do not.
attributesarray, nullThe product attributes with their values, and whether each one drives the variations.
variationCountinteger, nullHow many variations the product has, for example sizes or colours; 0 for a simple product.
imageCountinteger, null
imageUrlstring, nullThe first image; images holds all of them.
imagesarray, null
weightstring, nullFilled when the shop fills in a weight; it is an optional field in WooCommerce.
shortDescriptionstring, nullFilled when includeDescription is on; off by default because it makes rows much larger.
descriptionstring, nullFilled when includeDescription is on; off by default because it makes rows much larger.
storeProductTotalinteger, nullHow many products the shop has in total, so a run can be compared with the whole catalogue.
statusstring, nullEither 'ok' or 'error'. An error row carries errorCode and error, and leaves the data fields empty; it is never charged.
inputstring, nullThe input this row was built from, so a row can always be traced back.
errorCodestring, nullFilled on an error row only; a delivered row leaves it empty.
errorstring, nullFilled on an error row only; a delivered row leaves it empty.

Related datasets

Already using this one? Ratings are the first thing other buyers look at, and this dataset has none yet. If it does a job for you, a rating on its Apify page is the one thing that helps. It takes a minute and it is the only thing we ask for.

Open the Apify page