Skip to main content

Products API

The Products resource lives in the ec/v1 namespace. Reads are public; writes require manage_options or the wpec_manage_products capability. The OpenAPI spec is authoritative for exact field lists — this page shows the shapes you hit most.

List products

GET /ec/v1/products

Query parameters

All are optional. Multiple filters combine; attribute filters are ANDed by default (filter_logic).

ParameterTypeDefaultDescription
pageint1Page number
per_pageint20Items per page (max 100)
q / searchstringSearch term (q preferred; search accepted)
statusstringdraft · active · archived
typestringsimple · variable
categorystringCategory slug filter
category_idintCategory ID filter
brand_idintSingle brand ID
brand_idsstringComma-separated brand IDs
min_pricefloatMinimum price
max_pricefloatMaximum price
on_saleboolfalseOnly products currently on sale
min_ratingfloatMinimum average rating
stock_statusstringFilter by stock status
attr_{slug}stringAttribute filter, e.g. attr_color=red,blue (comma = OR within the attribute)
filter_logicstringandand / or — how multiple attribute filters combine
orderbystringidSort field
orderstringDESCASC / DESC
note

category (slug) and category_id (numeric ID) are different parameters — pick the one matching what you hold. When neither stock_status nor a search is set and the store's out-of-stock visibility setting is off, out-of-stock products are excluded automatically.

Response

List responses use the { data, meta } envelope (not a success wrapper):

{
"data": [
{
"id": 1,
"title": "Product Name",
"slug": "product-name",
"type": "simple",
"status": "active",
"sku": "PROD-001",
"price": "29.99",
"sale_price": null,
"stock_status": "in_stock",
"stock_quantity": 50,
"featured_image": { "id": 42, "url": "https://…", "thumbnail": "https://…", "medium": "https://…", "large": "https://…" },
"image": "https://…",
"categories": [ { "id": 1, "name": "Category" } ],
"category_ids": [1]
}
],
"meta": { "page": 1, "per_page": 20, "total": 150, "total_pages": 8 },
"inventory_settings": { "stock_display_format": "low_stock", "low_stock_threshold": 5 }
}

inventory_settings is included so a storefront can render stock badges consistently. Public (non-admin) list requests without a search term are cached.

Get a single product

GET /ec/v1/products/{id}

Returns the full product object directly (no wrapper). For variable products the response includes the enabled variants (with computed min/max price); it always includes featured_image, image_sources (AVIF/WebP when present), gallery_images, categories, and category_ids.

By slug

GET /ec/v1/products/slug/{slug}

Same shape as get-by-ID — useful for headless storefront routing.

Barcode lookup (admin)

GET /ec/v1/products/lookup?barcode=5201234567890&warehouse_id=0

Admin-only (wpec_manage_products). Resolves a product or variant by EAN/barcode across ean + variant barcode — used by the stocktake scanner. warehouse_id is optional (0 = default).

Create product

POST /ec/v1/products

Requires manage_options or wpec_manage_products.

{
"title": "New Product",
"type": "simple",
"status": "draft",
"sku": "NEW-001",
"ean": "5201234567890",
"brand": "Acme",
"product_condition": "new",
"short_description": "Brief summary",
"description": "Full description here",
"price": 39.99,
"regular_price": 39.99,
"sale_price": 29.99,
"stock_quantity": 100,
"track_inventory": true,
"low_stock_threshold": 5,
"category_ids": [1, 3],
"featured_image_id": 42,
"gallery_image_ids": [43, 44],
"weight": 0.5
}

title is the only required field. type defaults to simple, status to draft, product_condition to new. See the OpenAPI spec for the complete schema (also accepts mpn, attributes, length/width/height).

Update product

PUT /ec/v1/products/{id}
PATCH /ec/v1/products/{id}

Partial updates — send only the fields that changed. All fields optional.

Delete product

DELETE /ec/v1/products/{id}

Requires wpec_manage_products. Returns { "message": "Product deleted successfully" } with 204.

Duplicate product

POST /ec/v1/products/{id}/duplicate

Duplicates the product as a draft — copies fields, categories, images, and attributes, and generates a unique SKU/slug. Returns the new product (201). Admin-only.

GET /ec/v1/search?q=shoe&per_page=8

Public. A weighted product and category search (separate from the list filter). Returns a combined shape:

{
"products": { "items": [ /* … */ ], "total": 12 },
"categories": { "items": [ /* … */ ] }
}

per_page is capped at 20 (default 8). Results pass through the wpec_search_results filter.

Variants

Variable products expose variant CRUD + drag-reorder (all writes require wpec_manage_products):

MethodEndpointPurpose
GET/ec/v1/products/{id}/variantsList a product's variants (public)
POST/ec/v1/products/{id}/variantsCreate a variant
DELETE/ec/v1/products/{id}/variantsDelete all variants of the product
POST/ec/v1/products/{id}/variants/reorderReorder — body { "order": [ { "id": 1, "sort_order": 0 }, … ] }
GET/ec/v1/variants/{id}Read a single variant (public)
PUT / PATCH/ec/v1/variants/{id}Update a variant
DELETE/ec/v1/variants/{id}Delete a single variant

Filters

Applied while building API responses:

FilterSignaturePurpose
wpec_api_product_response($data, $product)Transform a single product response
wpec_api_products_response($data, $args, $total)Transform the product list array
wpec_product_query_args($args)Modify the underlying product query args
wpec_products_per_page(int $default)Default page size when per_page is absent
wpec_search_results($results, $query)Transform /search output

Where to go next