wc_get_products() and WC_Product_Query are the correct way to retrieve products in WooCommerce. They are functionally equivalent to get_posts and WP_Query in WordPress core, and they accept an array of arguments to define your query criteria.
Avoid using WP_Query or direct database queries for WooCommerce products. WooCommerce’s database structure changes between versions (most recently with HPOS – High-Performance Order Storage), and direct queries may break after updates.
wc_get_products and WC_Product_Query Examples
Here are some common examples. The code comments explain what each query does:
// Get downloadable products created in the year 2016.
$products = wc_get_products( array(
'downloadable' => true,
'date_created' => '2016-01-01...2016-12-31',
) );
// Get 10 most recent product IDs in date descending order.
$query = new WC_Product_Query( array(
'limit' => 10,
'orderby' => 'date',
'order' => 'DESC',
'return' => 'ids',
) );
$products = $query->get_products();
// Get products containing a specific SKU.
// Does partial matching, so this will get products with SKUs "PRDCT-1", "PRDCT-2", etc.
$query = new WC_Product_Query();
$query->set( 'sku', 'PRDCT' );
$products = $query->get_products();
WC_Product_Query Methods
These are the methods available on a WC_Product_Query instance:
- get_query_vars() – Get an array of all the current query variables set on the query object.
- get( string $query_var, mixed $default = ” ) – Get the value of a query variable or the default if the query variable is not set.
- set( string $query_var, mixed $value ) – Set a query variable to a value.
- get_products() – Get all products matching the current query variables.
Parameters
General
status – Accepts a string or an array of strings: one or more of the following options: ‘draft’, ‘pending’, ‘private’, ‘publish,’ or custom status.
// Get draft products.
$args = array(
'status' => 'draft',
);
$products = wc_get_products( $args );
type – Accepts a string or an array of strings: one or more of the following options: ‘external’, ‘grouped’, ‘simple’, ‘variable,’ or custom type.
// Get external products.
$args = array(
'type' => 'external',
);
$products = wc_get_products( $args );
include – Accepts an array of integers: include only product IDs that are in this array.
// Get external products limited to ones with specific IDs.
$args = array(
'type' => 'external',
'include' => array( 134, 200, 210, 340 ),
);
$products = wc_get_products( $args );
exclude – Accepts an array of integers: exclude product IDs that are in this array.
// Get products that aren't the current product.
$args = array(
'exclude' => array( $product->get_id() ),
);
$products = wc_get_products( $args );
parent – Accepts an integer: the ID of the parent product.
// Get products with a specific parent.
$args = array(
'parent' => 20,
);
$products = wc_get_products( $args );
parent_exclude – Accepts an array of integers: exclude products with parent IDs that are in this array.
// Get products that don't have parent IDs of 20 or 21.
$args = array(
'parent_exclude' => array( 20, 21 ),
);
$products = wc_get_products( $args );
limit – Accepts an integer: maximum number of results to retrieve or -1 for unlimited.
// Get the latest 3 products.
$args = array(
'limit' => 3,
);
$products = wc_get_products( $args );
page – Accepts an integer: the page of results that the function should work on. Irrelevant if the offset parameter is in use.
// First 3 products.
$args = array(
'limit' => 3,
'page' => 1,
);
$page_1_products = wc_get_products( $args );
// Second 3 products.
$args = array(
'limit' => 3,
'page' => 2,
);
$page_2_products = wc_get_products( $args );
paginate – Responsible for pagination – accepts a boolean variable, True or False. Default is False.
// Get products with extra info about the results.
$args = array(
'paginate' => true,
);
$results = wc_get_products( $args );
echo $results->total . ' products foundn';
echo 'Page 1 of ' . $results->max_num_pages . 'n';
echo 'First product id is: ' . $results->products[0]->get_id() . 'n';
offset – Accepts an integer: the offset for the product results.
// Get the second to fifth most recent products.
$args = array(
'limit' => 4,
'offset' => 1
);
$products = wc_get_products( $args );
order – Accepts a string: the parameters ‘DESC’ or ‘ASC’. Use it in conjunction with ‘orderby’. Default is ‘DESC’.
// Get most recently modified products.
$args = array(
'orderby' => 'modified',
'order' => 'DESC',
);
$products = wc_get_products( $args );
orderby – Accepts a string: the parameters are ‘none’, ‘ID’, ‘name’, ‘type’, ‘rand’, ‘date’, ‘modified’. Default is ‘date’.
// Get some random products.
$args = array(
'orderby' => 'rand',
);
$products = wc_get_products( $args );
return – Accepts a string: the parameters are ‘ids’ or ‘objects’. Default is ‘objects’.
// Get product ids.
$args = array(
'return' => 'ids',
);
$products = wc_get_products( $args );
Product
sku – Accepts a string: checks if the string exists in the product’s SKU.
// Get products with "PRDCT" in their SKU (e.g., PRDCT-1 and PRDCT-2).
$args = array(
'sku' => 'PRDCT',
);
$products = wc_get_products( $args );
tag – Accepts an array: limits the results to products with specific tags based on their slug.
// Get products with the "Excellent" or "Modern" tags.
$args = array(
'tag' => array( 'excellent', 'modern' ),
);
$products = wc_get_products( $args );
category – Accepts an array: limits the results to products with specific categories based on their slug.
// Get shirts.
$args = array(
'category' => array( 'shirts' ),
);
$products = wc_get_products( $args );
weight, length, width, height – Accepts float values: the dimensions the products should have.
// Get products 5.5 units wide and 10 units long.
$args = array(
'width' => 5.5,
'length' => 10,
);
$products = wc_get_products( $args );
price, regular_price, sale_price – Accepts float values: the price at which the results should be.
// Get products that currently cost 9.99.
$args = array(
'price' => 9.99,
);
$products = wc_get_products( $args );
total_sales – Accepts an integer: retrieves products with a specific number of sales.
// Get products that have never been purchased.
$args = array(
'total_sales' => 0,
);
$products = wc_get_products( $args );
virtual, downloadable, featured, sold_individually, manage_stock, reviews_allowed – Accepts a Boolean variable: limits results to products with specific settings or features.
// Get downloadable products that don't allow reviews.
$args = array(
'downloadable' => true,
'reviews_allowed' => false,
);
$products = wc_get_products( $args );
backorders – Accepts a string: options are ‘yes’, ‘no’, or ‘notify’.
// Get products that allow backorders.
$args = array(
'backorders' => 'yes',
);
$products = wc_get_products( $args );
visibility – Accepts a string: options are ‘visible’, ‘catalog’, ‘search’, or ‘hidden’.
// Get products that show in the catalog.
$args = array(
'visibility' => 'catalog',
);
$products = wc_get_products( $args );
stock_quantity – Accepts an integer: the quantity of the product in stock.
// Get products that only have one left in stock.
$args = array(
'stock_quantity' => 1,
);
$products = wc_get_products( $args );
stock_status – Accepts a string: the status of the products in stock – ‘outofstock’ or ‘instock’.
// Get out of stock products.
$args = array(
'stock_status' => 'outofstock',
);
$products = wc_get_products( $args );
tax_status – Accepts a string: options are ‘taxable’, ‘shipping’, or ‘none’.
// Get taxable products.
$args = array(
'tax_status' => 'taxable',
);
$products = wc_get_products( $args );
tax_class – Accepts a string.
// Get products in the "Reduced Rate" tax class.
$args = array(
'tax_class' => 'reduced-rate',
);
$products = wc_get_products( $args );
shipping_class – Accepts a string or an array of strings.
// Get products in the "Bulky" shipping class.
$args = array(
'shipping_class' => 'bulky',
);
$products = wc_get_products( $args );
download_limit, download_expiry – Accepts an integer: the restriction on the number of downloads, where -1 means unlimited.
// Get products with unlimited downloads.
$args = array(
'download_limit' => -1,
);
$products = wc_get_products( $args );
average_rating – Accepts a float: the average rating of the product.
// Get products with all 5-star ratings.
$args = array(
'average_rating' => 5.0,
);
$products = wc_get_products( $args );
review_count – Accepts an integer: the number of reviews for the product.
// Get products with 1 review.
$args = array(
'review_count' => 1,
);
$products = wc_get_products( $args );
Date
date_created, date_modified, date_on_sale_from, date_on_sale_to – Accepts a string: retrieves products based on the date with the format YYYY-MM-DD or TIMESTAMP.
// Get downloadable products created in the year 2016.
$products = wc_get_products( array(
'downloadable' => true,
'date_created' => '2016-01-01...2016-12-31',
) );
Add Custom Query Parameters
Use the woocommerce_product_data_store_cpt_get_products_query filter to add support for custom parameters:
/**
* Handle a custom 'customvar' query var to get products with the 'customvar' meta.
* @param array $query - Args for WP_Query.
* @param array $query_vars - Query vars from WC_Product_Query.
* @return array modified $query
*/
function handle_custom_query_var( $query, $query_vars ) {
if ( ! empty( $query_vars['customvar'] ) ) {
$query['meta_query'][] = array(
'key' => 'customvar',
'value' => esc_attr( $query_vars['customvar'] ),
);
}
return $query;
}
add_filter( 'woocommerce_product_data_store_cpt_get_products_query', 'handle_custom_query_var', 10, 2 );
Then use it like any built-in parameter:
$products = wc_get_products( array( 'customvar' => 'somevalue' ) );
Since WooCommerce 8.2, HPOS (High-Performance Order Storage) is enabled by default for new installations. While HPOS primarily affects orders (not products), it is a reminder that WooCommerce’s data layer evolves over time. Using
wc_get_products()instead of direct queries ensures your code stays compatible.
FAQs
Common questions about retrieving WooCommerce products:
wc_get_products() and WC_Product_Query?
wc_get_products() is a wrapper function that creates a WC_Product_Query internally. Use wc_get_products() for simple one-off queries, and WC_Product_Query when you need to modify the query object before executing it (for example, setting parameters dynamically with the set() method).WP_Query to get WooCommerce products?
WP_Query with post_type => 'product' technically works, it is not recommended. WooCommerce may change its underlying data storage between versions (as it did with HPOS for orders). Using wc_get_products() ensures your queries remain compatible with future WooCommerce updates.category parameter: wc_get_products( array( 'category' => array( 'shirts' ), 'limit' => -1 ) ). Set limit to -1 to retrieve all matching products without a cap.woocommerce_product_data_store_cpt_get_products_query filter to register a custom query variable, then pass it to wc_get_products(). Inside the filter callback, add a meta_query array targeting your custom meta key and value.wc_get_products() return product objects or IDs?
WC_Product objects. To get only product IDs (which is faster for large queries), set 'return' => 'ids' in the arguments array.Summary
wc_get_products() and WC_Product_Query are the recommended way to retrieve products in WooCommerce. They support a wide range of parameters for filtering by status, type, category, tag, price, stock, dates, and more. For custom filtering needs, the woocommerce_product_data_store_cpt_get_products_query filter lets you add your own query variables while keeping your code future-proof.

