Speeding Up WooCommerce API

I’ve been working with WooCommerce REST API for quite a while now, well, I developed a couple of plugins – Simple WordPress Crossposting and Simple Inventory Sync and both of them are relying on WooCommerce REST API.

And when you develop something that is intended to be used not only on your test website with 5 products total but also on high load websites, when orders can contain more than 100 products and variations, of course you should always keep performance in mind.

In this tutorial I will share with you some simple simple yet effective tips and tricks.

Use Batch Requests When Possible

The golden rule here is “The lesser REST API requests we have the better”. As an example let’s change the inventory of 10 products at the same time (we will make them out of stock).

Let’s take a look at two different examples of how it can be achieved.

Example 1. Loop the products and run REST API requests inside the loop

Let’s begin with the example how you should never do. Well, you can do it, if you want to change just a one single product.

// it is just an array of random product IDs
$product_ids = array( 1, 2, 3, 4, 7, 9, 10, 12, 15, 22 );

foreach( $product_ids as $product_id ) {
	
	wp_remote_request(
		"{$site_url}/wp-json/wc/v3/products/{$product_id}",
		array(
			'method' => 'PUT',
			'headers' => array(
				'Authorization' => 'Basic ' . base64_encode( "$login:$pwd" )
			),
			'body' => array(
				'stock_status' => 'outofstock'
			)
		)
	);

}

A code like this will result in trying to send 10 REST API requests inside a foreach() loop, which is insane. Ok, but what if we have 50 products? I guess “Fatal error: Maximum execution time…” (ERR_CONNECTION_TIMED_OUT) is what is waiting for you in that case.

WordPress fatal error maximum execution time

So, my first advise to you is when you’re working with one specific WooCommerce entity like products, customers, orders (sometimes – variations) please always consider to use batch requests. Below is an example of one.

Example 2. Do just one batch REST API request

What can I say here, a single request is always betten than 10 of them 😁

// it is just an array of random product IDs
$product_ids = array( 1, 2, 3, 4, 7, 9, 10, 12, 15, 22 );

$body = array(
	// we can update, create and delete products
	// right now we are just updating their stock statuses
	'update' => array(),
);
// add each product to the body of the request
foreach( $product_ids as $product_id ) {
	$body[ 'update' ][] = array(
		'id' => $product_id,
		'stock_status' => 'outofstock',
	);
}

wp_remote_request(
	"{$site_url}/wp-json/wc/v3/products/batch",
	array(
		'method' => 'POST',
		'headers' => array(
			'Authorization' => 'Basic ' . base64_encode( "$login:$pwd" )
		),
		'body' => $body
	)
);

In this example we also have a foreach() loop but we’re just using it to prepare the body for our batch request and of course we’re not running REST API requests inside the loop anymore.

Use the Appropriate WooCommerce Hooks

Depending on the things you want to accomplish the hooks and the code may be different. But I can show you an issue I faced with when developing my Simple Inventory Sync plugin and how I resolved it.

When I first created the plugin it used woocommerce_product_set_stock and woocommerce_variation_set_stock filter hooks to sync product stock information between stores. And these are great hooks… unless you have a lot of products inside a single order. I mean if a customer creates an order with for example twenty products in it then the woocommerce_product_set_stock hook is going to run twenty times! And we come back exactly to what I showed you in “the first example” – tons of REST API requests inside a loop.

What to do in this case? You need to explore WooCommerce core carefully and find some other hooks which are applied outside the same loop. In my case a great alternative solution was the woocommerce_reduce_order_stock hook, because it allows to loop order items and to create a body for a single batch request.

add_action( 'woocommerce_reduce_order_stock', function( $order ) {
	
	// get order items
	$items = $order->get_items( array( 'line_item' ) );
	if( ! $items ) {
		return;
	}
	
	$body = array(
		'update' => array(),
	);
	
	foreach( $items as $item ) {
		$body[ 'update' ][] = array(
			'id' => $item->get_product_id(),
			'stock_status' => 'outofstock',
			// some other product properties we may like to change
		);
	}
	
	// run batch REST API request here just a single time
	wp_remote_request( ... );
	
} );

Caching REST API Responses

In some cases a specific REST API request may return the same response (for a specific period of time or maybe even forever). Requests like this can be and should be cached.

For example we can try to get a product ID by its SKU from external site. And SKU is not that thing which is often changed, so we can cache a request like it.

function rudr_get_product_id_by_sku( $sku ) {
	
	if( false === $id = get_transient( '_product_id_for_' . $sku ) ) {
		
		// here we can run a REST API request to get a product ID by SKU
	
		set_transient( '_product_id_for_' . $sku, $id, WEEK_IN_SECONDS );
	}
	
	return $id;
	
}

Please note that:

clear WordPress transient cache with a WooCommerce tool

Unloading REST API Requests with WP Cron

Sometimes when you need to send multiple WooCommerce REST API requests, maybe it is better to send them one by one using WP Cron (and probably you’ll get better results when ALTERNATE_WP_CRON set to true).

In my case I work with product inventory, so I don’t think WP Cron is such a great idea here.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X