WooCommerce API – Product Sync with Multiple Stores
First of all we have to decide what it actually means – to sync products between sites? Let’s consider two scenarios here.
- Update a product with the same SKU on other WooCommerce stores automatically when it was updated by an administrator or a shop manager on the “Main store”. You can decide whether you would like to update a specific product data or all product data. And it is what we are going to do in this tutorial with code or with my WooCommerce product sync plugin.
- Update product stock status and quantity on other WooCommerce stores not only when this product was updated by an administrator but also when the product was purchased (and its quantity was descreased, obviously). We are going to talk a little bit about it in this tutorial as well, but I recommend you to take a look at my another plugin which is intended to help you with that.
In case you decide to use the plugin a metabox “Publish on” will appear.

Product Sync using WooCommerce REST API and save_post action hook
1. Prepare WooCommerce stores authentication data
I assume that you would like to sync products with multiple stores not with only one, so for each of them we have to prepare a set of data:
- Store URL,
- Login and Application Password.
I am going to use an array for that.
$stores = array(
// store 1
array(
'url' => '', // store URL goes here with https://
'login' => '',
'pwd' => '',
),
// store 2
array(
'url' => '',
'login' => '',
'pwd' => '',
),
// store 3 etc
);
It is fully up to you how you would like to process and store this data, but will definitely use $stores
array in the next chapter of this tutorial.
2. WooCommerce API: product sync with multiple stores when the one’s been updated
In this chapter we are going to create multiple WooCommerce REST API requests in order to sync (publish or update) a product when it was published or updated in admin.
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;
}
// that's an array of stores auth information we previously discussed
$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';
}
// do the sync
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;
Please keep in mind the following:
- Line
5
. We have a choice to use eithersave_post
orsave_post_{post type}
hook, I recommend the second one because it allows us to skip one more conditional statement. - Line
25
.$stores = array( … )
– is what we did in the previous step. You have to provide application passwords here. - Line
28
.$product->get_data()
is an amazing method ofWC_Product
class that allows to get all the product information and pass it almost without changes into a REST API request. - Lines
34-41
. More about sending API requests with product images you can find in this tutorial or take a look at the plugin solution. - Lines
49-53
. I am pretty sure you don’t want product duplicates to be created on other stores every time you hit “Update” button, so I created a methodproduct_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 with product meta data (and faster).
WooCommerce Product Sync Plugin
If the above code doesn’t fit your needs, you can configure WooCommerce API product sync with my Simple WordPress Crossposting plugin.
Some of the plugin features:
- Product images and product gallery images are fully supported.
- Easily add stores to sync products with in plugin settings.
- Sync not only simple but also variable products with their variations.
- Sync meta fields (ACF etc).
- Exclude specific product data from the syncronisation (e.g. product stock information).
Please let me know in comments if you have any questions about the code or about my plugin.

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
Hi, I would like to know the following about the plugin:
1- Does it comes with an auto-sync functionally to monitor shop A in case a new product is added or inventory changes replicate it in shop B?
2- If shop A does not have an SKU, is it possible to use ID instead?
3- I have 600 products in shop A, is it possible to use an auto-sync function or do I have to do it manually?
5- I have all the media already loaded in shop B, but I have deleted the products because I have been doing the job export/import manually. Do I have to delete all media in Shop B and then start syncing? Or the plugin automatically identifies the images from shop A (preloaded in B ) and attaches them to the new product created in shop B
Thanks
Hi Augusto,
1. Yes
2. The plugin sets a connection between products using meta field (you can choose either SKU or meta field in plugin settings).
3. Currently there is a bulk sync add-on but I have plans to create auto-sync tool.
5. Yes, better to delete those media (it is possible to make the automatic media identification of course but it will slow down the plugin, so I didn’t add that).
You’re always welcome if you have any other questions :)
Hi Misha.
First of all, thank you for the detailed description of the processes in your posts.
I’d like to know if where’s an easy way to get a product by ID via API and import it. I guess it would be similar to what you describe here, but from the “destination” store.
Thanks in advance!
Hi Esteban,
You can get a product by ID using this endpoint
/wp-json/wc/v3/products/<id>
, not sure whether it is an easy way though :)