search ]

Properly Retrieve WooCommerce Products in Your Template

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:

What is the difference between wc_get_products() and WC_Product_Query?
They are functionally equivalent. 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).
Can I use WP_Query to get WooCommerce products?
While 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.
How do I get all products in a specific category?
Pass the category slug in the category parameter: wc_get_products( array( 'category' => array( 'shirts' ), 'limit' => -1 ) ). Set limit to -1 to retrieve all matching products without a cap.
How do I query products by custom fields (meta)?
Use the 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.
Does wc_get_products() return product objects or IDs?
By default it returns an array of 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.

Join the Discussion
0 Comments  ]

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!

Savvy WordPress Development official logo