# WooCommerce Products: Catalog, Prices & Stock (`datalantern/woocommerce-products`) Actor

Export published WooCommerce product catalogs from the public Store API, with exact decimal prices, currency, stock, SKU and optional descriptions. No admin keys or customer data.

- **URL**: https://apify.com/datalantern/woocommerce-products.md
- **Developed by:** [Data Lantern](https://apify.com/datalantern) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## WooCommerce Public Product Catalog

Export public product catalogs from storefronts that expose WooCommerce's public Store API. Collect product identity, prices, and stock flags without store credentials. Not every WooCommerce site exposes this API.

### What can this tool do?

Export publicly visible product metadata from one or more supported WooCommerce storefronts. Use it to organize supplier catalogs, compare public prices, or track product availability over time.

- Read published, visible products through the public Store API without an admin key.
- Process multiple storefronts with one shared result limit.
- Optionally filter by a search term or sale status.
- Preserve currency precision instead of treating every minor-unit price as cents.
- Stop at source restrictions rather than logging in or trying to bypass a block.

It does not discover stores, retrieve orders or customers, access carts, or download product images.

### What data does it return?

One row per public product per store. Missing scalar values are `null`; list fields are arrays.

| Fields | Meaning |
|---|---|
| `storeUrl`, `productId` | Store identifier and the source's product ID. Use both together as a stable key. |
| `productName`, `productUrl`, `productType`, `sku` | Public catalog identity and link. |
| `price`, `regularPrice`, `salePrice` | Decimal price strings converted using the source's currency minor unit. Missing prices stay `null`; a real zero stays zero. |
| `priceMin`, `priceMax` | Optional price range for a variable product. |
| `currency`, `currencyMinorUnit` | Source currency code and decimal precision. |
| `inStock`, `onSale` | Source stock and sale flags, or `null`. |
| `description`, `shortDescription` | Plain-text product descriptions only when explicitly enabled; otherwise `null`. Personal email addresses are masked. |
| `imageUrls` | Up to 50 public image URLs; the images are not fetched. |
| `categories` | Up to 50 public category names. |

No customer, order, reviewer, or personal contact fields are exported. Password-protected or explicitly unpublished products are skipped.

### How to use it

Use a public storefront root, not its admin page or an API URL. Paste this JSON into the Actor input:

```json
{
  "stores": ["https://porterandyork.com"],
  "maxProducts": 100,
  "search": "",
  "onSale": false,
  "includeDescriptions": false
}
```

| Input | Default | Accepted values |
|---|---|---|
| `stores` | `["https://porterandyork.com"]` | 1–20 public storefront URLs, optionally including a WordPress subdirectory. Duplicate stores are removed. An empty array is invalid. |
| `maxProducts` | `100` | Integer 1–5,000 across all stores combined. |
| `search` | `""` | Optional product-search string of at most 200 characters. |
| `onSale` | `false` | `true` restricts the source query to sale products; `false` does not apply a sale-only filter. |
| `includeDescriptions` | `false` | Include public plain-text product descriptions when `true`. |

URLs must not contain credentials, query parameters, fragments, nonstandard ports, or admin/API paths. Private, local, and nonpublic network targets are rejected. Redirects to a different host or from HTTPS to HTTP are not followed; supply the public final storefront URL instead.

Earlier stores may consume the global result limit before later stores are reached. At most 50 catalog-page requests are made per run, with one second between catalog requests and up to 100 products requested per page. Separate robots.txt requests are not part of that 50-page count. Pagination stops on the source's last page, an empty page, a repeated page, the global result limit, or the request cap.

For recurring catalog monitoring, save rows in your own system under `storeUrl + productId`. Fetch a fresh snapshot and compare price, stock, and range fields. The tool does not retain past results, automatically detect deletions, or maintain a cross-run change log. A missing item in a capped or filtered run does not prove a product was deleted.

A readable catalog with no matching public products succeeds with zero rows and an explanatory note. An inaccessible, blocked, or disallowed store is stopped; other stores may still produce partial output. If no catalog can be read, the run fails rather than reporting a successful empty export.

### Pricing

Pay per result: **$1.00 per 1,000 products**, plus **$0.001 for a run returning at least one valid product**.

The one-time `actor-start` event costs $0.001 and is triggered only when the first valid product is ready. Each returned product incurs one `product` event at $0.001. Invalid input, entirely unreadable catalogs, and readable catalogs with no matching products emit neither event.

For example, 10 returned products incur $0.001 + 10 × $0.001 = **$0.011 in Actor event charges**. The start charge applies once across all stores, not once per store. Partial-success runs charge only for products actually returned, plus that single start charge.

### Is it legal?

The tool uses [WooCommerce's public Store API products endpoint](https://developer.woocommerce.com/docs/apis/store-api/resources-endpoints/products/). It does not request private management APIs, log in, or access customer and transaction data.

Before requesting catalog pages, it checks robots.txt for the requested API path. A failed robots check, a disallowed path, a challenge page, or an access restriction stops that store without bypassing it. Public API availability varies by site and can change.

An API response with HTTP 200 does not override robots.txt. During source validation, woocommerce.com returned catalog data but disallowed the catalog path for this client, so it is not used as the default. The Porter & York example passed the public API and robots preflight at the time of inspection; that does not guarantee future availability.

Public availability and robots rules do not settle every licensing or contractual question. Use only stores and data you are entitled to access, review the store's terms, and respect product-content and image rights. The tool returns image links, not a license to reuse images.

### FAQ

**Does it work with every WooCommerce store?**

No. A store may disable the public API, use an incompatible version or structure, block automated access, or restrict the path in robots.txt. Those cases are explained as source errors, not worked around with credentials or proxies.

**Why do I get fewer products than the shop advertises?**

Only the visible public catalog is eligible. Search filters, sale-only filtering, product visibility, source pagination, and the shared result/request caps can all reduce the output.

**Are prices numbers or strings?**

Prices are decimal strings. This avoids floating-point rounding and respects currencies with different minor units. A missing price is `null`, not zero; ranges remain separate `priceMin` and `priceMax` fields.

**Can I export descriptions?**

Yes, with `includeDescriptions: true`. It is disabled by default to keep exports smaller. Only public product text is used; protected products are skipped and personal email addresses in text are masked.

**Does an empty catalog fail?**

A valid public catalog response containing no matching products succeeds without start or product events. If every store is unreadable, the run fails with the source reason.

**Will a new snapshot tell me which items were deleted?**

Not by itself. Only compare complete, equivalent snapshots. A capped, filtered, partial, or failed export must not be treated as evidence that a product disappeared.

### Feedback

For an issue report, include the public storefront URL, input options, expected result, and source error or output note. Do not send store credentials, private APIs, customer data, or API keys.

# Actor input Schema

## `stores` (type: `array`):

1–20 public storefront roots or WordPress subdirectories. No credentials, query strings, private hosts or admin URLs.

## `maxProducts` (type: `integer`):

Global product cap across all stores, 1–5000. At most 50 catalog requests per run.

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

Optional store-side product search phrase, at most 200 characters.

## `onSale` (type: `boolean`):

Use the public Store API on-sale filter.

## `includeDescriptions` (type: `boolean`):

Optional plain-text descriptions. Personal email addresses are masked; descriptions are otherwise omitted.

## Actor input object example

```json
{
  "stores": [
    "https://porterandyork.com"
  ],
  "maxProducts": 100,
  "onSale": false,
  "includeDescriptions": false
}
```

# Actor output Schema

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

No description

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

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

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

```

## MCP server setup

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

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/qFoD53uaZOddxtXhi/builds/E7zz3auZsH7WCbqTq/openapi.json
