Sync WooCommerce Products Between Sites

Below, you will find two ways to configure product sync with multiple WooCommerce stores:

The idea is that when you create or update a product on one (primary) store, it will be automatically created or updated on connected WooCommerce stores. The two-way sync is also possible.

The WooCommerce Product Sync Plugin

Let’s begin with the product sync plugin approach. Still, continue reading this article if you want to learn how to use the WooCommerce API for product sync. For sure, the plugin uses the API as well, but you don’t need to code anything here or even know what it is and how it works under the hood.

I can recommend two options as a WooCommerce product sync plugin:

Product sync pluginHow it works
Simple WP CrosspostingThis is the full version of the plugin; it can sync not only WooCommerce products, but also posts, pages, and custom post types. It can also handle complex custom fields and more.
Simple Product Sync for WooCommerceThe free version of the plugin; it easily allows you to sync WooCommerce products. However, if you’re using complex ACF fields, or maybe you want to bulk sync multiple products, then consider using the full version.

No matter which plugin version you’ve decided to use on your WooCommerce store, the whole process of product syncing is similar. When you edit a specific store, you can use checkboxes to select one of the connected stores to which you want to sync this product:

WooCommerce sync products between sites using classic product editor
It is not always necessary, because the plugin has the “Auto Mode” feature.

Multiple WooCommerce products sync (using WordPress bulk actions)

Another cool feature of the plugin is the possibility to sync multiple WooCommerce products at the same time using WordPress bulk actions. However, it is only available in the Pro version.

Here is how this feature is going to look in your store:

WooCommerce API product sync for multiple products
Basically, you can select as many products as you want and start syncing them with a selected store.

Exclude product data from WooCommerce API product sync

One of the cool things about the plugin is that you can easily exclude unneeded product data from syncing, for example, product stock, prices, or even product statuses (which basically allows you to create products as drafts on other stores).

This can be easily achieved on the plugin settings page:

Exclude product data from WooCommerce API product sync in the plugin settings.
In this screenshot, I don’t want to sync sale prices and related products (Upsells and Cross-sells).

More plugin features:

  • Of course, product images and product gallery images are fully supported.
  • Sync not only simple products but also variable products with variations and other types of products.
  • Sync meta fields – ACF and other plugins are supported (but for complex custom fields you will need the Pro version).
  • Compatibility with many popular third-party plugins like WPML, Polylang, Elementor, Yoast, JetEngine, etc. (tested specifically in the Pro version).
  • Prices (and other product data) can be modified during the synchronization process using the rudr_swc_pre_crosspost_product_data hook.

You can take a closer look at my WooCommerce product sync plugin, which also allows you to work with regular WordPress posts, pages, and custom post types.

Feel free to take a closer look at my WooCommerce product sync plugin, whether you choose the Pro or Lite version.

Using WooCommerce REST API to Sync Products Between Sites

Maybe you don’t need the product sync plugin, or maybe you’re creating some custom functionality for your client store – either way, I am going to describe step-by-step how to use the WooCommerce API for product sync with multiple WooCommerce stores.

1. Prepare authentication data from your target stores

I assume you would like to perform the product sync with multiple stores (not only with a single one), so for each of them, we have to prepare a set of data. Depending on the chosen way of syncing products, it could be different:

WordPress HTTP API product syncWooCommerce API product sync
Store URLStore URL
Username (login)Consumer key
Application passwordConsumer secret

Now, let’s specify it in our application. In this tutorial, I am going to use PHP and store the authentication data as an array.

// an array of multiple WooCommerce stores you would like to sync products between
$stores = array(
	// store 1
	array(
		'url' => '', // the store URL goes here with https://
		'login' => '', // a username or a consumer key
		'pwd' => '', // an application password or a consumer secret
	),
	// store 2
	array(
		'url' => '',
		'login' => '',
		'pwd' => '',
	),
	// store 3 etc
);

In case you’re going to use the WordPress HTTP API, provide an application password in the pwd parameter, or for WooCommerce API product sync, you need to provide a consumer key in the login parameter and consumer secret in the pwd parameter.

2. Decide how you’re going to interact with WooCommerce REST API

Previously, I already said that I am going to use PHP in order to interact with WooCommerce REST API, and it is perfectly fine if you’re about to do the same.

But I think I have to tell you that there are different ways of how you can do that:

It is absolutely up to you to decide which method you’re going to use. In this tutorial, I will show you both WordPress HTTP API and WooCommerce API product sync methods in PHP.

3. Sync products between sites using the REST API

In this chapter, we are going to create multiple REST API requests to sync (publish or update) a product on connected stores automatically when it is published or updated on the main store.

/*
 * Plugin name: WooCommerce API Product Sync with Multiple Stores
 * Plugin URI: https://rudrastyh.com/woocommerce/product-sync-with-multiple-stores.html
 * Version: 1.0.0
 * Author: Misha Rudrastyh
 */
class Misha_Sync_Woo_Products {

	public function __construct() {

		add_action( 'save_post_product', array( $this, 'sync' ), 99, 2 );

	}

	public function sync( $product_id, $post ) {

		// do nothing if WooCommerce is not installed
		if( ! function_exists( 'wc_get_product' ) ) {
			return;
		}

		// get product object
		$product = wc_get_product( $product_id );

		// do the sync for published products only
		if( 'publish' !== $product->get_status() ) {
			return;
		}

		// do the product sync with multiple WooCommerce stores from this array
		$stores = array( ... );

		// prepare product data before the loop
		$product_data = $product->get_data();
		// Fix: "Error 400. Bad Request. Cannot create existing product."
		unset( $product_data[ 'id' ] );
		// Fix: "Error 400 Bad Request low_stock_amount is not of type integer,null." error
		$product_data[ 'low_stock_amount' ] = (int) $product_data[ 'low_stock_amount' ];
		// In order to sync a product image we have to do some additional stuff
		if( $product_data[ 'image_id' ] ) {
			$product_data[ 'images' ] = array(
				array(
					'src' => wp_get_attachment_url( $product_data[ 'image_id' ] )
				)
			);
			unset( $product_data[ 'image_id' ] );
		}

		// let's loop through multiple stores and sync the product with each of them
		foreach( $stores as $store ) {

			$endpoint = "{$store[ 'url' ]}/wp-json/wc/v3/products";
			$method = 'POST';

			// let's check if the product with the same SKU already exists
			if( $product_id_2 = $this->product_exists( $product, $store ) ) {
				$endpoint = "{$endpoint}/{$product_id_2}";
				$method = 'PUT';
			}

			// WooCommerce API product sync request
			wp_remote_request(
				$endpoint,
				array(
					'method' => $method,
					'headers' => array(
						'Authorization' => 'Basic ' . base64_encode( "{$store[ 'login' ]}:{$store[ 'pwd' ]}" )
					),
					'body' => $product_data
				)
			);

		}

	}

	private function product_exists( $product, $store ) {

		$sku = $product->get_sku();

		$request = wp_remote_get(
			add_query_arg( 'sku', $sku, "{$store[ 'url' ]}/wp-json/wc/v3/products" ),
			array(
				'headers' => array(
					'Authorization' => 'Basic ' . base64_encode( "{$store[ 'login' ]}:{$store[ 'pwd' ]}" )
				)
			)
		);

		if( 'OK' === wp_remote_retrieve_response_message( $request ) ) {
			$products = json_decode( wp_remote_retrieve_body( $request ) );
			if( $products ) {
				$product = reset( $products );
				return $product->id;
			}
		}

		return false;

	}

}

new Misha_Sync_Woo_Products;

Keep in mind the following:

  • When working with WooCommerce products, you have a choice to use either save_post or save_post_{post type} hook, I recommend the second one because it allows you to skip one more conditional statement.
  • $stores = array( … ) we created in the previous step. You have to provide application passwords here.
  • $product->get_data() is an amazing method of WC_Product class that allows you to get all the product information and pass it almost without changes into a REST API request.
  • In this example, I only sync the main product image, but if you need to sync gallery images as well, I recommend using my plugin.
  • I’m pretty sure you don’t want product duplicates to be created on other stores every time you hit the “Update” button, so I created a method product_exists() that performs one more REST API request in order to check whether a product with the same SKU already exists or not. But it is also possible to do it with product metadata (and faster).

Now, let’s take a look at using the WooCommerce API for product sync instead of the WordPress HTTP API. To do so, I am just going to slightly change the sync() method:

foreach( $stores as $store ) {
	
	$woocommerce = new Client( $store[ 'url' ], $store[ 'login' ], $store[ 'pwd' ] ) );

	if( $product_id_2 = $this->product_exists( $product, $woocommerce ) ) {
		
		try{
			$woocommerce->put( "products/{$product_id_2}", $product_data );
		} catch( Exception $error ) {
			// processing errors here
		}
		
	} else {
		
		try{
			$woocommerce->post( "products", $product_data );
		} catch( Exception $error ) {
			// processing errors here
		}
		
	}
}

Please don’t forget that in this case, we’re using a Consumer Key as $store[ 'login' ] and a Consumer Secret as $store[ 'pwd' ].

We also need to completely change the product_exists() method:

private function product_exists( $product, $woocommerce ) {

	$sku = $product->get_sku();
	$products = $woocommerce->get( 'products', array( 'sku' => $sku ) );
	if( $products ) {
		$product = reset( $products );
		return $product->id;
	}
	return false;

}

Please let me know in the comments if you have any questions about the code or about my plugin.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 15 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