# WooCommerce Products Scraper (`bluefin/woocommerce-products-scraper`) Actor

Unofficial tool, not affiliated with or endorsed by Automattic or the WooCommerce project. Extracts products and variations from public WooCommerce stores via the Store API (/wp-json/wc/store/v1/products). Flat dataset output with currency-coded prices, HTTP-only (no browser).

- **URL**: https://apify.com/bluefin/woocommerce-products-scraper.md
- **Developed by:** [Bluefin](https://apify.com/bluefin) (community)
- **Categories:** E-commerce, Developer tools
- **Stats:** 2 total users, 1 monthly users, 96.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 product scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## 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 Products Scraper

*This is an unofficial tool. It is not affiliated with, endorsed by, or sponsored by Automattic Inc. or the WooCommerce open-source project. "WooCommerce" is a trademark of Automattic Inc.*

Extract the full product catalog from any **public WooCommerce store** — every product and, if you want, every variation — as clean, flat rows you can download as JSON, CSV, or Excel. It reads the store's public **WooCommerce Store API** (`/wp-json/wc/store/v1/products`), so it is fast and does not run a browser.

Point it at one or more store URLs (for example `https://cyberchimps.com`) and it walks the whole catalog page by page, then hands you names, slugs, SKUs, **currency-coded prices** (regular, sale, and min/max for variable products), on-sale flags, stock status, categories, images, product types, and canonical product URLs. Running it on the [Apify platform](https://apify.com) adds scheduled runs, API access, proxy rotation, and integrations with Make, Zapier, Google Sheets, and more.

#### Why use this WooCommerce scraper?

- **Competitor and price monitoring** — track a competitor's catalog, price points, and what they mark as on sale. Unlike many storefront scrapers, prices come with a **currency code** (e.g. `USD`, `EUR`).
- **Catalog research** — pull an entire store's assortment for analysis, dropshipping research, or building a product feed.
- **Availability tracking** — see which products are in stock, purchasable, or on backorder at scrape time.
- **Lightweight and cheap** — HTTP-only (no headless browser), so runs are quick and use little compute.

#### How to use it

1. Add one or more WooCommerce store URLs to **Start URLs**. You can paste the store home page (`https://store.com`) or any URL on the store — only the domain is used.
2. Optionally set **Max products per store** or toggle **Expand variations into separate rows**.
3. Leave **Proxy configuration** on Apify Proxy (recommended — see the note under Limitations).
4. Click **Start**. When the run finishes, open the **Output** tab or export the dataset as JSON, CSV, or Excel.

#### Input

| Field                 | Type    | Default     | Description                                                                                      |
| --------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------ |
| `startUrls`           | array   | (required)  | WooCommerce store URLs. Home page or any store URL is accepted; only the domain is used.         |
| `maxProductsPerStore` | integer | `0`         | Cap on products per store. `0` means no limit. Counted per product, not per variation.           |
| `includeVariants`     | boolean | `true`      | Expand each variation of a variable product into its own row. When `false`, one row per product. |
| `proxyConfiguration`  | object  | Apify Proxy | Proxy settings.                                                                                  |

Example input:

```json
{
    "startUrls": [{ "url": "https://cyberchimps.com" }],
    "maxProductsPerStore": 0,
    "includeVariants": true,
    "proxyConfiguration": { "useApifyProxy": true }
}
```

#### Output

Each row is one product, or one variation of a variable product when `includeVariants` is `true`. Real example rows from a live run:

```json
{
    "store_url": "https://cyberchimps.com",
    "product_id": 583076,
    "name": "Ficus Bonsai",
    "slug": "ficus-bonsai",
    "permalink": "https://cyberchimps.com/store/ficus-bonsai/",
    "sku": "Ficus",
    "type": "simple",
    "on_sale": false,
    "price": "20.00",
    "regular_price": "20.00",
    "sale_price": "20.00",
    "price_min": null,
    "price_max": null,
    "currency_code": "USD",
    "is_in_stock": true,
    "is_purchasable": true,
    "is_on_backorder": false,
    "low_stock_remaining": null,
    "categories": "Cactus",
    "image_url": "https://cyberchimps.com/wp-content/uploads/2024/09/nature-leaf-product-07.jpg",
    "variation_id": null,
    "variation_attributes": null
}
```

A variation row from a variable product (note the `price_min`/`price_max` range and the variation fields):

```json
{
    "store_url": "https://cyberchimps.com",
    "product_id": 567238,
    "name": "Responsive Pro (Plugin)",
    "type": "variable",
    "price": "59.00",
    "price_min": "59.00",
    "price_max": "119.00",
    "currency_code": "USD",
    "is_in_stock": true,
    "categories": null,
    "variation_id": 567239,
    "variation_attributes": "API Key Type: 1 Site"
}
```

You can download the dataset in JSON, HTML, CSV, or Excel from the Output tab or the API.

##### Data fields

| Field                                                | Description                                                                              |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `store_url`                                          | Origin of the store the row came from.                                                   |
| `product_id`                                         | WooCommerce product ID.                                                                  |
| `name`                                               | Product name.                                                                            |
| `slug`                                               | URL slug of the product.                                                                 |
| `permalink`                                          | Canonical product page URL.                                                              |
| `sku`                                                | Product SKU, or `null` if the store did not set one.                                     |
| `type`                                               | Product type: `simple`, `variable`, `grouped`, `subscription`, etc.                      |
| `on_sale`                                            | Whether the product is currently on sale.                                                |
| `price` / `regular_price` / `sale_price`             | Decimal prices (converted from the API's minor-unit values using the currency exponent). |
| `price_min` / `price_max`                            | Price range for variable products; `null` for simple products.                           |
| `currency_code`                                      | ISO currency code for the prices, e.g. `USD`, `EUR`.                                     |
| `is_in_stock` / `is_purchasable` / `is_on_backorder` | Stock status flags at scrape time.                                                       |
| `low_stock_remaining`                                | Remaining units when the store exposes a low-stock threshold, otherwise `null`.          |
| `categories`                                         | Comma-joined category names.                                                             |
| `image_url`                                          | First product image, if present.                                                         |
| `variation_id`                                       | Variation ID when a variable product is expanded, otherwise `null`.                      |
| `variation_attributes`                               | Variation attributes as text, e.g. `"Size: L, Color: Red"`, otherwise `null`.            |

#### How much does it cost to scrape WooCommerce?

This Actor uses **pay-per-event** pricing with two charges:

- **Run start** — charged once each time a run starts (Apify's built-in start event).
- **Product scraped** — charged once per product stored. Variations of the same variable product do **not** add extra charges, so turning variation expansion on does not increase cost.

A store with 500 products is billed as one run start plus 500 product charges, no matter how many variations those products have. So a product with 20 size/color variations is billed once, not 20 times. Check the Actor's Store page for the current per-charge amounts.

The run-start charge is counted per gigabyte of memory: it is charged once for runs up to and including 1 GB of RAM, then once more for each additional GB. This Actor runs HTTP-only and sets a 1 GB default, so under the default it is a single start charge.

> **For developers / publishers — important, read before publishing.** Billing is **per product**, achieved with a single custom event named `product-result` (defined in `src/main.ts` and `.actor/pay_per_event.json`). Apify's pay-per-event model also offers two automatic "synthetic" events, and one of them is a trap for this Actor:
>
> - Use the synthetic **`apify-actor-start`** for run startup (Apify charges it automatically — do not charge it from code, that fails).
> - You **must remove** the synthetic **`apify-default-dataset-item`** event in the Monetization wizard. If left enabled, it auto-charges once per row in the default dataset — and this Actor writes one row per variation, which would bill a 20-variation product 20 times and make the "no extra charge for variations" promise above false.
>
> Pay-per-event pricing is configured in the Apify Console Monetization wizard, not in `actor.json`. Follow `PUBLISH_CHECKLIST.md` exactly, and after publishing run the billing verification step there to confirm no per-row charges appear.

#### Limitations (please read)

- **Public data only.** It reads the store's public Store API — the same JSON any browser can request. It cannot access password-protected content, unpublished products, admin-only fields, or exact inventory counts.
- **The store must expose the WooCommerce Store API.** The Store API (`/wp-json/wc/store/v1/products`) ships with modern WooCommerce and is public by default, but some stores run an old WooCommerce version, have the REST route disabled, or sit behind a plugin/CDN that blocks it. Such stores return **404** (or an HTML page) and are **skipped with a clear reason** — they are not scraped. In practice a meaningful share of live WooCommerce sites block or disable this endpoint, so expect some stores in a batch to be skipped.
- **Per-variation price and SKU are not available.** The Store API's product **list** endpoint returns each variation as an ID plus its attributes (e.g. `Size: L`) only — it does **not** include per-variation price or SKU. This Actor does not make an extra request per variation, so variation rows carry the product-level price and the `price_min`/`price_max` range, not a distinct per-variation price. This keeps runs fast and cheap.
- **Some stores block datacenter IPs.** Many WooCommerce stores sit behind Cloudflare or bot walls and rate-limit or block datacenter traffic (HTTP 429/403, or an HTML "loading" page instead of JSON). Keeping **Apify Proxy** enabled (the default) rotates IPs and materially improves success rates. If a store still returns 429, try residential proxy groups.
- **Skipped stores are recorded, not hidden.** Each skip is logged with a reason and stored in the `SUMMARY` record in the run's key-value store. If every store is unreachable, the run fails rather than reporting a false success.
- **This Actor does not evaluate robots.txt automatically.** You are responsible for confirming the target store's `robots.txt`, terms of service, and applicable laws before scraping it. Some stores disallow the `/wp-json/` path in their `robots.txt` even though it responds; check each store you intend to scrape.

#### FAQ

**Is scraping WooCommerce stores legal?**
This Actor only requests the store's own public Store API endpoint — publicly available product data. This Actor does not evaluate robots.txt automatically; you are responsible for confirming the target store's `robots.txt`, terms, and applicable laws before scraping. It targets product catalog data only and does not collect customer or account data. Note that catalog fields such as a brand or vendor name can occasionally contain a person's name (for example, a sole trader's brand), so treat the output accordingly.

**Why did a store return 0 products or get skipped?**
The store may not expose the Store API (old WooCommerce version, endpoint disabled, or not a WooCommerce site), it may be blocking your IP, or it may serve an HTML page instead of JSON. Check the `SUMMARY` record in the run's key-value store for the exact per-store reason, and try enabling Apify Proxy.

**Does it get every product?**
It paginates through the Store API (100 products per page) until the store returns an empty page, honoring the `X-WP-TotalPages` header, so it retrieves the full public catalog unless you set `maxProductsPerStore`.

**How is the price formatted?**
The WooCommerce Store API returns prices in the currency's minor units (e.g. `5900` for $59.00). This Actor converts them to a decimal string using the currency's minor-unit exponent and includes the `currency_code`.

**How do I report a problem?**
Use the **Issues** tab on the Actor page.

# Actor input Schema

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

One or more WooCommerce store URLs. You can pass the store home page (e.g. https://cyberchimps.com) or any URL on the store; only the domain is used. Each store is scraped independently via its public Store API.

## `maxProductsPerStore` (type: `integer`):

Upper limit of products fetched per store. Set to 0 for no limit (fetch the whole catalog). Counting is per product, not per variation.

## `includeVariants` (type: `boolean`):

When enabled, each variation of a variable product becomes its own dataset row (with its own variation ID and attributes, e.g. 'Size: L'). The WooCommerce Store API list endpoint does NOT expose per-variation price or SKU, so those stay at the product level (see the Actor README). When disabled, one row per product is emitted.

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

Proxy settings. Apify Proxy (automatic) is recommended: many WooCommerce stores sit behind Cloudflare or bot walls that rate-limit or block datacenter IPs, so rotating proxies materially improve success rates.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://cyberchimps.com"
    }
  ],
  "maxProductsPerStore": 0,
  "includeVariants": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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": [
        {
            "url": "https://cyberchimps.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bluefin/woocommerce-products-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": [{ "url": "https://cyberchimps.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("bluefin/woocommerce-products-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": [
    {
      "url": "https://cyberchimps.com"
    }
  ]
}' |
apify call bluefin/woocommerce-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bluefin/woocommerce-products-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/0uv7MMCDV3PPt61Ex/builds/TvAFNNO3xfvr0I3S7/openapi.json
