Skip to main content

Products

The Products resource provides full CRUD for WooCommerce products.

Endpoints​

MethodPathDescription
GET/woo/productsList products
GET/woo/products/{id}Get product
POST/woo/productsCreate product
PUT / PATCH/woo/products/{id}Update product
DELETE/woo/products/{id}Delete product

List query parameters​

ParameterTypeDefaultDescription
fieldsstringdefault setComma-separated field names
statusstring|array—Filter by status (comma-separated)
typestring—Filter by product type
skustring—Exact SKU match
searchstring—Text search
stock_statusstring—instock, outofstock, onbackorder
sortstring-date_createdSort field, prefix - for DESC
pageint1Page number
per_pageint20Items per page (max 100)

Allowed sort fields: date_created, date_modified, id, title

Available fields​

List defaults: id, name, slug, status, type, sku, price, stock_status, date_created, date_modified

All fields: id, name, slug, status, type, sku, price, regular_price, sale_price, date_created, date_modified, catalog_visibility, description, short_description, stock_status, stock_quantity, manage_stock, virtual, downloadable, meta_data

Since 1.0.0: price is read-only — it is a derived field that WooCommerce computes from regular_price/sale_price (and the scheduled-sale sync). It is still returned in responses, but sending it in a create/update payload is rejected with 400 validation_failed; set regular_price/sale_price instead. Monetary fields are serialized as decimal strings (e.g. "29.99").

Create / Update payload​

{
"name": "Premium Widget",
"status": "publish",
"type": "simple",
"sku": "WDG-001",
"regular_price": "29.99",
"sale_price": "24.99",
"description": "Full product description.",
"short_description": "A premium widget.",
"stock_status": "instock",
"stock_quantity": 50,
"manage_stock": true,
"virtual": false,
"downloadable": false,
"catalog_visibility": "visible",
"meta_data": [
{ "key": "brand", "value": "WidgetCo" }
]
}

Notes​

  • type is create-only. It cannot be changed after creation. Defaults to simple when omitted.
  • Boolean fields (manage_stock, virtual, downloadable) accept booleans, integers (0/1), or strings ("true", "false", "yes", "no").
  • Since 1.1.1, stock_quantity accepts a finite number (including negative/fractional values) or null, provided Woo's stock normalizer preserves the value.

Since 1.1.0:

  • The whole payload is validated before persistence: string fields must be strings, regular_price/sale_price must be empty or a non-negative number, stock_quantity is validated before setters (see the 1.1.1 stock rules below), and boolean fields reject values outside the accepted forms — all with 400 validation_failed.
  • List sorting always appends an ID tie-breaker, so pagination is deterministic when many products share the same sort value.
  • The WooProductInput OpenAPI schema no longer advertises the read-only price field, matching runtime behavior.

Since 1.0.0:

  • The search parameter maps to the supported s query var. (Earlier versions passed an unsupported search var that WooCommerce silently ignored, returning unfiltered results.)
  • sort=price was removed — WooCommerce's product query does not reliably order by price, so it is no longer advertised and returns 400 validation_failed.
  • Invalid input rejected by a WooCommerce CRUD setter (WC_Data_Exception) is returned as 400, not 500.

v0.3.0 changes​

  • deleteMode ('force' default or 'trash') on the registrar controls whether DELETE permanently removes or trashes the product.
  • Protected meta keys (_...) are not returned and not writable by default.

Stock quantities (1.1.1)​

Finite numeric strings are accepted as well as numbers; booleans, non-numeric values and non-finite numbers are rejected. If wc_stock_amount() changes a requested value, the write returns 400 validation_failed instead of silently truncating it. Negative stock and null remain valid. Fractional stores must keep the same Woo stock configuration for future reads/writes. OpenAPI stock fields use number or null.