# Woocommerce Scraper (`scraper-engine/woocommerce-scraper`) Actor

WooCommerce Scraper extracts product data from WooCommerce stores with accuracy. Collect product names, prices, SKUs, categories, images, and availability for research, monitoring, and catalog building. Ideal for eCommerce analysis and automation.

- **URL**: https://apify.com/scraper-engine/woocommerce-scraper.md
- **Developed by:** [Scraper Engine](https://apify.com/scraper-engine) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 14 total users, 2 monthly users, 97.6% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$19.99/month + usage

To use this Actor, you pay a monthly rental fee to the developer. The rent is subtracted from your prepaid usage every month after the free trial period. You also pay for the Apify platform usage, which gets cheaper the higher Apify subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#rental-actors

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### WooCommerce Scraper — Products, Prices, Stock and Reviews

WooCommerce Scraper reads any WooCommerce store's public Store API and returns its products as structured JSON — name, SKU, price in real currency units, stock status, average rating, review count, images, categories, tags and attributes. Switch the **Resource** input and the same run pulls categories, brands, tags, attributes, reviews, pages or posts instead. No login, API key or WooCommerce REST API credentials are required. Start a run below with a single store URL.

***

### What is WooCommerce Scraper?

WooCommerce Scraper is an Apify Actor that reads a WooCommerce store's public **WooCommerce Store API** (`/wp-json/wc/store/*`) and **WordPress REST API** (`/wp-json/wp/v2/*`) and returns the results as structured JSON — one row per product, category, review, page or whichever resource you select. No WooCommerce account, REST API consumer key/secret or store login is required: it reads the same public endpoints the store's own shop page calls in a browser. It's built for competitor-pricing teams, dropshippers, catalogue researchers and AI engineers who need a store's data as clean, typed JSON rather than parsed HTML.

***

### What WooCommerce product data is publicly available to scrape?

Any WooCommerce store running version 4.0 or newer publishes its product catalogue, taxonomy and reviews through the Store API without requiring a login. Order, customer and checkout data are never public — they sit behind the store's own wp-admin session or a WooCommerce REST API key the store owner has to issue.

| Data category | Publicly available | Restricted |
| --- | --- | --- |
| Product listing (name, price, stock, images) | ✅ `wc/store/products` | — |
| Product taxonomy (categories, tags, attributes) | ✅ | Brands taxonomy only if the WooCommerce Brands plugin is installed — otherwise `brands` answers `200` with an empty list |
| Product reviews via the Store API `reviews` resource | ✅ | — |
| Product reviews via WordPress `wp/v2/comments` | ⚠️ Hidden from logged-out clients | WooCommerce filters review-type comments out of that endpoint — use `resource: "reviews"` instead |
| Pages and posts (`wp/v2/pages`, `wp/v2/posts`) | ✅ | — |
| Registered users (`wp/v2/users`) | ✅ on some stores | ❌ Disabled (`404`) on many hardened stores |
| Draft, private or trashed products | ❌ | Never exposed — the Store API only publishes what a shopper can see |
| Orders, customers, coupons, checkout | ❌ | wp-admin login or a WooCommerce REST API consumer key/secret |

WooCommerce Scraper only returns publicly visible data — what any shopper's browser already loads. Nothing behind a login wall, an admin session or a REST API key.

***

### What data can I extract with WooCommerce Scraper?

Each product row carries identity, taxonomy and content fields alongside computed pricing, stock and rating metrics. Every value comes straight from the store's own API response — a field the store didn't send comes back `null`, never a guess.

#### 🏷️ Identity, taxonomy and content fields

| Field | Description |
| --- | --- |
| `url` | Product permalink |
| `id` | WooCommerce product ID |
| `name` | Product name (HTML entities decoded) |
| `slug` | URL slug |
| `parent` | Parent product ID (`0` for a standalone product; the parent's ID on a variation row) |
| `type` | `simple`, `variable`, `grouped`, `external` or `variation` |
| `variation` | Variation attribute values, present on variation rows |
| `sku` | Store-assigned SKU |
| `short_description` / `description` | Rendered as Markdown, plain text or raw HTML, per the **Output Format** input |
| `categories` / `tags` / `brands` / `attributes` | Arrays of taxonomy term objects exactly as the store publishes them |
| `images` | Array of image objects (`src`, `thumbnail`, `srcset`, `alt`, …) |
| `variations` | Array of variation ID/attribute pairs (expand into full rows with **Include Variations**) |
| `grouped_products` | Child product IDs on a `grouped` product |
| `store` / `resource_type` | The store URL this row came from, and the resource it was fetched under |

#### 📊 Pricing, stock and rating fields

| Field | Description |
| --- | --- |
| `prices` | Raw store object: `price`, `regular_price`, `sale_price`, `price_range`, `currency_code`, `currency_symbol`, `currency_minor_unit`, `currency_decimal_separator`, `currency_thousand_separator`, `currency_prefix`, `currency_suffix` |
| `price_value` / `regular_price_value` / `sale_price_value` | The prices above divided by the store's own minor unit, as real numbers (`60.0`, not `"60"`) |
| `currency_code` | ISO currency code, flattened alongside `price_value` for convenience |
| `on_sale` | `true`/`false` |
| `average_rating` | String exactly as the store publishes it, e.g. `"4.50"` |
| `review_count` | Integer, kept even when it's `0` |
| `is_in_stock` / `is_on_backorder` | Stock flags |
| `low_stock_remaining` | Units left, or `null` if the store doesn't publish low-stock counts |
| `sold_individually` | `true` if only one unit can be bought per order |
| `stock_availability` | `{ text, class }` stock-status label the store shows on the page |
| `has_options` / `is_purchasable` | Variable-product and purchasability flags |
| `add_to_cart` | `{ minimum, maximum, multiple_of, single_text, url }` order-quantity rules |
| `extensions` | Whatever WooCommerce or third-party plugin extension blocks the store attaches (e.g. name-your-price data), or `{}` |

Switching **Resource** away from `products` returns `categories`, `brands`, `tags`, `attributes`, `reviews`, `pages`, `posts`, `comments`, `post-categories`, `post-tags` or `users` exactly as the store's own API publishes each record, with `store` and `resource_type` added to every row.

#### 🤖 Add-on: Need data from other storefronts too?

If you're benchmarking a WooCommerce store against Shopify competitors in the same category, pair this with **Shopify Products Scraper** for the equivalent catalogue read on Shopify storefronts.

***

### How does WooCommerce Scraper differ from the official WooCommerce REST API?

WooCommerce's own REST API (`wp-json/wc/v3`) requires the store owner to generate a consumer key and secret in **WooCommerce → Settings → Advanced → REST API** before a single request works, and every call must authenticate with that key pair ([woocommerce.github.io, checked 2026-08-16](https://woocommerce.github.io/woocommerce-rest-api-docs/)). That makes it unusable for reading a store you don't own or administer — exactly the case for competitor research, price monitoring and market surveys.

| Feature | WooCommerce REST API (`wc/v3`) | WooCommerce Scraper |
| --- | --- | --- |
| Read a store you don't own | ❌ needs a consumer key/secret the store owner issues you | ✅ reads the same public Store API a shopper's browser calls |
| Setup | Store owner generates keys; you configure Basic Auth or OAuth1.0a | Paste a store URL and run |
| Multiple unrelated stores in one run | ❌ separate key pair per store | ✅ one run, any number of URLs in `startUrls` |
| Published rate limit | Not published in WooCommerce's own docs | Self-paced, with automatic proxy escalation only when a store pushes back |
| Data returned | Whatever the key's scope allows, including orders and customers | Products, taxonomy, reviews and WordPress content — public storefront data only, never orders or customers |
| Output shape | Raw `wc/v3` REST objects you normalize yourself | Prices pre-converted to real currency units alongside the raw store values |

Use the WooCommerce REST API when you own the store and need write access, orders or customer data. Use WooCommerce Scraper for read-only catalogue collection across stores you don't control, without asking the merchant for a key.

***

### How to use WooCommerce Scraper

1. Open WooCommerce Scraper on the Apify Store and click **Try for free** (or **Start**, if you already have it saved) to open the input form in Apify Console.
2. Fill in `startUrls` with at least one store address — a bare domain (`https://example.com`) or a sub-directory install (`https://example.com/shop`) both work.
3. Optionally set `resource`, `sort`, `stock`, `category`, `min_price`/`max_price` or the other product filters to narrow what comes back.
4. Click **Start**.
5. Open the run's dataset and export it as JSON, CSV, Excel, XML or HTML, or pull it through the Apify API.

#### How to scale to bulk product extraction

`startUrls` is an array — add as many store addresses as you need in one run, and each gets its own `store` value on every row. For a store list too long to paste in, point `dev_fileupload` at a hosted text or JSON file listing one URL per line (or a JSON array); its URLs are merged into the run automatically.

***

### What can you do with WooCommerce product data?

- 💰 **Competitor price monitoring** — a pricing analyst schedules a daily run across rival stores and diffs `price_value` and `on_sale` per `sku` to catch markdowns as they happen.
- 📦 **Inventory and backorder tracking** — an ops analyst watches `is_in_stock`, `is_on_backorder` and `low_stock_remaining` to see what a competitor is about to sell out of.
- 🗂️ **Catalogue and PIM enrichment** — a catalogue manager pulls `name`, `description`, `images`, `categories` and `attributes` to seed or refresh an internal product database.
- ⭐ **Review mining** — a CX researcher sets `resource: "reviews"` to collect rating, reviewer and review text for sentiment analysis without touching the storefront HTML.
- 🤖 **AI shopping-assistant / RAG pipelines** — an AI engineer indexes `name`, `description` and `attributes` into a vector store so an agent can answer product questions or compare SKUs across stores without re-scraping. The typed JSON output is a ready tool response for any agent framework that can call the Apify API.

***

### How does WooCommerce Scraper handle rate limits and blocking?

Requests start with no proxy. If a store refuses a direct request, the run escalates automatically — Apify datacenter proxy first, then Apify residential proxy, retrying up to 3 times on the residential tier with a 2-second pause between attempts. Once a tier answers `200` it is kept for the rest of the run. A `4xx` response that isn't a block signal (anything other than `401`, `403`, `407` or `429`) is treated as the request's own fault and returned immediately rather than escalated, since a bigger proxy budget can't fix a bad filter value. If an endpoint answers `200` with a body that isn't valid JSON, that's reported as the store likely serving an HTML challenge page rather than silently returning an empty result. Between stores in a multi-store run, the actor pauses one second. No CAPTCHA solving is performed.

***

### ⬇️ Input

Required per schema: none — but a run needs at least one store URL in `startUrls` or `url` to produce data.

| Parameter | Required | Type | Description | Example value |
| --- | --- | --- | --- | --- |
| `startUrls` | No | array | Store home pages. A bare domain works, and a sub-directory install is kept. Fill this or `url`. | `["https://porterandyork.com/"]` |
| `url` | No | array | Alternative to `startUrls`. | `[]` |
| `limit` | No | integer | Max records per store (1–1000, default `10`). Variations from **Include Variations** are returned on top of it. | `50` |
| `resource` | No | string | `products` (default), `categories`, `brands`, `tags`, `attributes`, `reviews`, `pages`, `posts`, `comments`, `post-categories`, `post-tags`, `users`. | `"products"` |
| `include_variations` | No | boolean | Also fetch every variation of the variable products found (default `false`). | `true` |
| `format` | No | string | `md` (default), `text` or `html` — for `description`, `short_description` and review bodies. | `"md"` |
| `sort` | No | string | `date` (default), `modified`, `id`, `include`, `title`, `slug`, `price`, `popularity`, `rating`, `menu_order`, `comment_count`, or `""` for the store's own order. Unsupported values for the chosen resource are logged and skipped. | `"price"` |
| `order` | No | string | `""` (default, auto), `asc` or `desc`. Applied only when `sort` is supported by the resource. | `"asc"` |
| `search` | No | string | Text search, run by the store. Not supported for `attributes` or `reviews`. | `"steak"` |
| `sku` | No | string | Exact SKU. Products only. | `"PY1261"` |
| `rating` | No | string | `""` (default, any), `1`–`5`. Products only; the Store API honours one value. | `"5"` |
| `min_price` | No | integer | Minimum price, in the store's minor currency unit (`1999` = 19.99 on a 2-decimal store). Products only. | `1000` |
| `max_price` | No | integer | Maximum price, same minor-unit rule. Products only. | `5000` |
| `category` | No | string | Comma-separated category term IDs. Run once with `resource: "categories"` to get them. Products only. | `"21,34"` |
| `tag` | No | string | Comma-separated tag term IDs. Products only. | `"12"` |
| `product_type` | No | string | `""` (default, any), `simple`, `grouped`, `external`, `variable`, `wbs_bundle`, `variation`. Products only. | `"variable"` |
| `stock` | No | string | `""` (default, any), `instock`, `outofstock`, `onbackorder`. Products only. | `"instock"` |
| `featured` | No | boolean | Featured products only (default `false`). Products only. | `false` |
| `sale` | No | boolean | On-sale products only (default `false`). Products only. | `true` |
| `proxyConfiguration` | No | object | Apify proxy settings. By default no proxy is used; the actor escalates on its own if a store refuses the direct request. | `{"useApifyProxy": false}` |
| `dev_proxy_config` | No | string | Your own `http(s)://user:pass@host:port` proxy, used instead of the Apify proxy. SOCKS is not supported. | `""` |
| `dev_custom_headers` | No | string | Extra HTTP headers: a JSON array of `{name, value}`, a JSON object, or `Name: value` lines. | `""` |
| `dev_custom_cookies` | No | string | Extra HTTP cookies, same three accepted shapes. | `""` |
| `dev_transform_fields` | No | string | Comma-separated field paths to keep in the output, e.g. `id,name,prices.price`. | `""` |
| `dev_dataset_name` | No | string | Write to a named dataset instead of the default one. Masks: `{ACTOR}`, `{DATE}`, `{TIME}`. | `""` |
| `dev_dataset_clear` | No | boolean | Empty the named dataset above first (default `false`). No effect without `dev_dataset_name`. | `false` |
| `dev_no_strip` | No | boolean | Keep `null`/empty-string/empty-array/empty-object fields in the output (default `false`). Booleans and zeros are always kept. | `false` |
| `dev_fileupload` | No | string | URL of a text or JSON file holding one store URL per line, or a JSON array of URLs. | `""` |

⚠️ `min_price`/`max_price` match a variable product if **any** of its variations falls in the range — so a `50`–`70` filter can return a product whose own `price_value` is `15`. Set `include_variations: true` to get one row per variation with its own price if you need an exact match.

#### Example input

```json
{
  "startUrls": ["https://porterandyork.com/", "https://mercantile.wordpress.org/"],
  "limit": 100,
  "resource": "products",
  "sale": true,
  "stock": "instock",
  "max_price": 5000,
  "sort": "price",
  "order": "asc",
  "include_variations": true,
  "format": "md",
  "proxyConfiguration": { "useApifyProxy": false }
}
```

***

### ⬆️ Output

Results land in the Actor's dataset as typed JSON, exportable as JSON, CSV, Excel, XML or HTML from Apify Console or via the API. Each record written to the dataset is billed once under the `row_result` pricing event; a store that doesn't answer, or a resource that returns no records, is never charged.

#### Example output

```json
{
  "url": "https://porterandyork.com/product/buy-porterhouse-steak-online/",
  "id": 15,
  "name": "Porterhouse Steak",
  "slug": "buy-porterhouse-steak-online",
  "parent": 0,
  "type": "variable",
  "sku": "PY1261",
  "short_description": "A cross between the tenderloin and the strip.",
  "description": "Hand-cut and dry-aged porterhouse steak, USDA Prime.",
  "on_sale": false,
  "prices": {
    "price": "6000",
    "regular_price": "6000",
    "sale_price": "6000",
    "price_range": { "min_amount": "6000", "max_amount": "9500" },
    "currency_code": "USD",
    "currency_symbol": "$",
    "currency_minor_unit": 2,
    "currency_decimal_separator": ".",
    "currency_thousand_separator": ",",
    "currency_prefix": "$",
    "currency_suffix": ""
  },
  "price_value": 60.0,
  "regular_price_value": 60.0,
  "sale_price_value": 60.0,
  "currency_code": "USD",
  "average_rating": "4.50",
  "review_count": 12,
  "images": [
    { "id": 1234, "src": "https://porterandyork.com/wp-content/uploads/porterhouse.jpg", "alt": "" }
  ],
  "categories": [
    { "id": 21, "name": "Beef", "slug": "beef", "link": "https://porterandyork.com/product-category/beef/" }
  ],
  "tags": [],
  "brands": [],
  "attributes": [
    { "id": 1, "name": "Size", "taxonomy": "pa_size", "has_variations": true, "terms": [] }
  ],
  "variations": [
    { "id": 1230, "attributes": [{ "name": "Size", "value": "24-oz-usda-prime" }] }
  ],
  "grouped_products": [],
  "has_options": true,
  "is_purchasable": true,
  "is_in_stock": true,
  "is_on_backorder": false,
  "low_stock_remaining": null,
  "sold_individually": false,
  "stock_availability": { "text": "", "class": "in-stock" },
  "add_to_cart": {
    "minimum": 1,
    "maximum": 9999,
    "multiple_of": 1,
    "single_text": "Add to cart",
    "url": "https://porterandyork.com/product/buy-porterhouse-steak-online/"
  },
  "extensions": {},
  "store": "https://porterandyork.com/",
  "resource_type": "products"
}
```

Other resources (`categories`, `brands`, `tags`, `attributes`, `reviews`, `pages`, `posts`, `comments`, `post-categories`, `post-tags`, `users`) are written to the same dataset shape-for-shape as the store's own API publishes them, with `store` and `resource_type` added to every row.

***

### How does it work?

WooCommerce Scraper calls the same public JSON endpoints a browser loads when it visits a WooCommerce storefront — the Store API (`/wp-json/wc/store/*`) for products and product taxonomy, and the WordPress REST API (`/wp-json/wp/v2/*`) for pages, posts, comments and users. No browser is launched and no JavaScript is rendered; these are direct HTTP calls. Each store URL is normalised so both a bare domain and a sub-directory install resolve, and the chosen resource is paged with `per_page`/`offset` until your `limit` is reached. Requests start on a direct connection and only route through an Apify datacenter or residential proxy if the store pushes back. Only publicly visible data is ever returned — nothing behind a login. Because every WooCommerce store publishes this API in the same shape, the output schema stays the same regardless of which theme or page builder the store is running.

***

### Integrations

WooCommerce Scraper works with the tools you already use for automation and analysis — no plugin required, since it's just an Apify Actor reachable over the standard Apify API.

#### Calling WooCommerce Scraper programmatically

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run = client.actor("scraper-engine/woocommerce-scraper").call(run_input={
    "startUrls": ["https://porterandyork.com/"],
    "limit": 100,
    "resource": "products",
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["name"], item.get("price_value"))
```

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request against the Apify API.

#### No-code tools (n8n, Make, LangChain)

In **n8n**, use the HTTP Request node pointed at the Actor's run-sync-get-dataset-items endpoint with your API token, or the dedicated Apify node if you have it installed. In **Make**, the Apify app's "Run an Actor" module accepts the same `startUrls`/`resource` input and returns the dataset items to the next module. In **LangChain**, the `ApifyWrapper`/Apify Actor tool can call this Actor and load its dataset directly as `Document` objects for a retrieval pipeline.

***

### Is it legal to scrape WooCommerce products?

Scraping publicly available product data is generally lawful — this Actor returns only what any shopper's browser can already load from a storefront, without bypassing a login, password or paywall. Product catalogues, prices and SKUs are business/product data, not personal data, so GDPR and CCPA do not attach to this Actor's core output; the relevant framework instead is the store's terms of service and whatever database-rights protections apply to a compiled catalogue in your jurisdiction. If you set `resource: "reviews"`, review text is content the reviewer chose to publish on the store; treat any reviewer name in it accordingly. Consult legal counsel if your use case involves bulk storage or redistribution of a store's data.

***

### Frequently asked questions

#### What WooCommerce product fields does WooCommerce Scraper return?

The top fields are `name`, `sku`, `price_value` (with `regular_price_value`/`sale_price_value`), `is_in_stock` and `average_rating`. See [What data can I extract](#what-data-can-i-extract-with-woocommerce-scraper) above for the full field tables.

#### Does WooCommerce Scraper require a WooCommerce account or login?

No. It reads the store's public Store API and WordPress REST API exactly as a shopper's browser would — no account, consumer key/secret or store login is needed.

#### How many products can I extract in one run?

Up to `limit` per store (1–1000, default 10). Setting `include_variations: true` adds every variation of the variable products found on top of that number.

#### What happens if a product is out of stock or removed from the store?

An out-of-stock product still comes back, with `is_in_stock: false`, `is_on_backorder` set accordingly and `low_stock_remaining` populated if the store publishes it. A product that has been unpublished, put back into draft or trashed never appears at all — the Store API only exposes what a shopper can currently see, so there's no separate "removed" status to check for.

#### Can I scrape multiple WooCommerce stores at once?

Yes. Put every store URL in `startUrls`, or point `dev_fileupload` at a hosted file listing them. Each row carries its `store` URL so you can tell rows from different stores apart in one dataset.

#### Does WooCommerce Scraper work with Claude, ChatGPT and other AI agent tools?

Yes, as an HTTP endpoint any agent framework can call through the Apify API — the JSON output is typed and consistent, so it drops straight into a tool call or RAG pipeline without extra parsing.

#### How does WooCommerce Scraper compare to other WooCommerce scrapers?

Most WooCommerce scrapers only read the product listing. WooCommerce Scraper's `resource` switch also reads categories, brands, tags, attributes, reviews and the store's WordPress pages, posts and comments from the same run, and it tracks which `sort` and filter values each endpoint actually accepts — a request that would otherwise get an HTTP 400 from the store is detected and skipped, with a log line, instead of silently returning zero rows.

#### Does WooCommerce Scraper return data in a format LLMs can use directly?

Yes. Typed, normalized JSON with consistent field names across runs — no HTML parsing, no selectors. Pass it directly to an LLM, index it into a vector store, or feed it to an agent tool.

#### What happens when a WooCommerce store changes its theme or anti-bot setup?

The Store API and WordPress REST API this Actor reads are WooCommerce's own stable data endpoints, not the rendered storefront page, so a theme change doesn't change the output schema. No specific update turnaround is published for anti-bot changes on a store's side.

#### Can I use WooCommerce Scraper without managing proxies or browser infrastructure?

Yes. There's no browser to configure — requests are direct HTTP calls — and escalation through an Apify datacenter or residential proxy happens automatically if a store pushes back, with no proxy setup required unless you want to supply your own.

#### Which fields work best for AI training data and RAG indexing?

For RAG, index `name`, `description` and `short_description` as descriptive text alongside `attributes` and `categories` for structured lookups. For training data, `price_value`, `average_rating`, `review_count` and `is_in_stock` are the most consistently structured fields across every product row. All fields return as typed primitives — strings, numbers, booleans or arrays — never mixed types.

***

### Related scrapers

| Scraper | What it extracts |
| --- | --- |
| Shopify Products Scraper | A Shopify store's full product catalogue: variants, SKUs, price and stock |
| Shopify Store Scraper | Store-level profile: theme, installed apps, catalogue size |
| Shopify Merchant Scraper | Store contact details, socials and tech stack |
| Alibaba Listings Scraper | Alibaba product listings: prices, MOQ, supplier details |
| Amazon Product Details Scraper | Product details from Amazon listing pages |
| eBay Product Listing Scraper | Product data from eBay search and listing pages |

***

### Your feedback

Found a bug, or missing a field you need from a WooCommerce store? Open an issue on this Actor's **Issues** tab in Apify Console — that's the fastest way to reach the maintainer directly.

# Actor input Schema

## `startUrls` (type: `array`):

💡 Which stores do you want to read? Enter store home pages (https://example.com). A bare domain works, and a sub-directory install (https://example.com/shop) is kept. Fill in this field or the 'URL (alternative)' field below.

## `url` (type: `array`):

Alternative to startUrls: array of store URLs to scrape.

## `limit` (type: `integer`):

Maximum number of records to return per store. Product variations fetched by 'Include Variations' are returned on top of this number.

## `resource` (type: `string`):

Which record type to read. 'products' is the full product row; every other value is returned exactly as the store's API publishes it. 'brands' needs the WooCommerce Brands taxonomy installed, 'users' is disabled on many stores, and 'comments' hides product reviews from logged-out clients - use 'reviews' for those.

## `include_variations` (type: `boolean`):

Also return every variation of the variable products found (one extra request per variation). Variation rows carry the parent product id in 'parent' and type 'variation'.

## `format` (type: `string`):

💡 Output format for descriptions and review bodies. (default: Markdown)

## `sort` (type: `string`):

Sort order requested from the store. Products accept every value below. Categories, tags and brands accept Slug, Title and Popularity only; reviews accept Date, ID and Rating; pages and posts accept Date, Modified, ID, Title and Slug. An unsupported value is reported in the log and the store's own order is used.

## `order` (type: `string`):

Sort direction. Applied only when the chosen Sorting value is supported by the resource.

## `search` (type: `string`):

💡 Return only records matching this text. Applied by the store itself. Not supported for the 'attributes' and 'reviews' resources.

## `sku` (type: `string`):

💡 Return only products with this SKU. Products resource only.

## `rating` (type: `string`):

💡 Return only products whose rounded average rating equals this many stars. The WooCommerce Store API honours one value, so a single star level is applied. Products resource only.

## `min_price` (type: `integer`):

Minimum price, in the store's minor currency unit (cents for a 2-decimal store: 1999 means 19.99). Products resource only.

## `max_price` (type: `integer`):

Maximum price, in the store's minor currency unit (cents for a 2-decimal store: 4999 means 49.99). Products resource only.

## `category` (type: `string`):

Product category ID(s), comma separated. Run the actor once with resource 'categories' to get the IDs. Products resource only.

## `tag` (type: `string`):

Product tag ID(s), comma separated. Run the actor once with resource 'tags' to get the IDs. Products resource only.

## `product_type` (type: `string`):

Return only products of this type. Products resource only.

## `stock` (type: `string`):

Return only products with this stock status. Products resource only.

## `featured` (type: `boolean`):

Return only products the store marks as featured. Products resource only.

## `sale` (type: `boolean`):

Return only products that are currently on sale. Products resource only.

## `proxyConfiguration` (type: `object`):

Choose which proxies to use. By default no proxy is used; if a store refuses the direct request the actor escalates to an Apify datacenter proxy and then to a residential proxy on its own.

## `dev_proxy_config` (type: `string`):

💡 Your own HTTP(S) proxy, used instead of the Apify proxy.
http://{user:pass}@{hostname|ip-address}:port
Example: http://user:pass@proxy.example.com:8000
SOCKS proxies are not supported by this actor's HTTP client.

## `dev_custom_headers` (type: `string`):

💡 Extra HTTP headers, as a JSON array of {name, value} pairs, a JSON object, or one 'Name: value' per line.
Example: \[{"name": "Authorization", "value": "Bearer token"}]

## `dev_custom_cookies` (type: `string`):

💡 Extra HTTP cookies, as a JSON array of {name, value} pairs, a JSON object, or one 'name=value' per line.
Example: \[{"name": "session", "value": "abc123"}]

## `dev_transform_fields` (type: `string`):

💡 Keep only these fields in the output. Enter comma-separated field paths; each path becomes one flat column.
For a nested object use a DOT, e.g. prices.price
For an array element use its index, e.g. images.0.src

## `dev_dataset_name` (type: `string`):

💡 Write the results into a named dataset instead of the default one. Masks:
{ACTOR} = actor name
{DATE} = date (YYYYMMDD)
{TIME} = time (HHMMSS)
Example: data-{DATE} becomes data-20260810

## `dev_dataset_clear` (type: `boolean`):

Empty the named dataset above before writing this run's results. It has no effect without 'CUSTOM STORAGE': the default dataset of a run is always new and empty.

## `dev_no_strip` (type: `boolean`):

💡 Keep empty values (null, empty string, empty array, empty object) in the output. Booleans and zeros are always kept.

## `dev_fileupload` (type: `string`):

💡 URL of a text or JSON file holding one store URL per line or a JSON array of URLs. Its URLs are added to the list above.

## Actor input object example

```json
{
  "startUrls": [
    "https://porterandyork.com/"
  ],
  "limit": 10,
  "resource": "products",
  "include_variations": false,
  "format": "md",
  "sort": "date",
  "order": "",
  "featured": false,
  "sale": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "dev_dataset_clear": false,
  "dev_no_strip": false
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped items in the Actor's default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://porterandyork.com/"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scraper-engine/woocommerce-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": ["https://porterandyork.com/"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scraper-engine/woocommerce-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://porterandyork.com/"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scraper-engine/woocommerce-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scraper-engine/woocommerce-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Ob7IzUrlI3drPO9Nd/builds/ngdZKFbMqNvxp4pNc/openapi.json
