Add and manage products

The Products sub-API lets you manage the entire lifecycle of your products in Merchant Center. You can use it to upload new products, update existing ones, retrieve product information, and delete products you no longer sell.

Special considerations

  • Products can only be inserted,updated or deleted if they belong to data sources of type API . You cannot insert or update products within data sources that use file-based uploads.
  • To keep your products relevant and prevent expiration , you should update or refresh them with a regular cadence (at least every 30 days).

Prerequisites

Before you can add or manage products using the API, you must have at least one data source configured in your Merchant Center account.

To create a data source, use the dataSources.create method from the Data Sources sub-API. For detailed instructions, see the Manage API data sources for product uploads guide. After creating a data source, take note of its name (for example, accounts/12345/dataSources/67890 ), as you'll need it for product-related creation, update, or deletion requests.

Add a product

To add a new product to your Merchant Center account, you need to insert a ProductInput resource into a primary data source. This action creates the product and starts processing. The fields within the productAttributes object must conform to the Product data specification .

Use the productInputs.insert method and provide the name of your primary data source as the dataSource parameter.

This example inserts a new product for an online store into a primary data source.

  POST https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs:insert?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
 
 
  { 
  
 "offerId" 
 : 
  
 "SKU12345" 
 , 
  
 "contentLanguage" 
 : 
  
 "en" 
 , 
  
 "feedLabel" 
 : 
  
 "US" 
 , 
  
 "productAttributes" 
 : 
  
 { 
  
 "title" 
 : 
  
 "Classic Cotton T-Shirt" 
 , 
  
 "description" 
 : 
  
 "A comfortable, durable, and stylish t-shirt made from 100% cotton." 
 , 
  
 "link" 
 : 
  
 "https://www.example.com/p/SKU12345" 
 , 
  
 "imageLink" 
 : 
  
 "https://www.example.com/img/SKU12345.jpg" 
 , 
  
 "availability" 
 : 
  
 "IN_STOCK" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "15990000" 
 , 
  
 // 15.99 USD 
  
 "currencyCode" 
 : 
  
 "USD" 
  
 }, 
  
 "condition" 
 : 
  
 "NEW" 
 , 
  
 "gtins" 
 : 
  
 [ 
 "9780007350896" 
 , 
  
 "93433295494587" 
 ] 
  
 } 
 } 
 

A successful call returns the newly created ProductInput resource. The name field contains the unique identifier for this product input, and the product field contains the name of the final, processed product you can later use to retrieve it.

  { 
  
 "name" 
 : 
  
 "accounts/ {ACCOUNT_ID} 
/productInputs/en~US~SKU12345" 
 , 
  
 "product" 
 : 
  
 "accounts/ {ACCOUNT_ID} 
/products/en~US~SKU12345" 
 , 
  
 "offerId" 
 : 
  
 "SKU12345" 
 , 
  
 "contentLanguage" 
 : 
  
 "en" 
 , 
  
 "feedLabel" 
 : 
  
 "US" 
 , 
  
 "productAttributes" 
 : 
  
 { 
  
 "title" 
 : 
  
 "Classic Cotton T-Shirt" 
 , 
  
 "description" 
 : 
  
 "A comfortable, durable, and stylish t-shirt made from 100% cotton." 
 , 
  
 "link" 
 : 
  
 "https://www.example.com/p/SKU12345" 
 , 
  
 "imageLink" 
 : 
  
 "https://www.example.com/img/SKU12345.jpg" 
 , 
  
 "availability" 
 : 
  
 "IN_STOCK" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "15990000" 
 , 
  
 "currencyCode" 
 : 
  
 "USD" 
  
 }, 
  
 "condition" 
 : 
  
 "NEW" 
 , 
  
 "gtins" 
 : 
  
 [ 
  
 "9780007350896" 
  
 ] 
  
 } 
 } 
 

The following code samples show how to add a product.

Java

  import 
  
 com.google.api.gax.core.FixedCredentialsProvider 
 ; 
 import 
  
 com.google.auth.oauth2.GoogleCredentials 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Availability 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Condition 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.InsertProductInputRequest 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductAttributes 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInput 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputsServiceClient 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputsServiceSettings 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Shipping 
 ; 
 import 
  
 com.google.shopping.type.Price 
 ; 
 import 
  
 shopping.merchant.samples.utils.Authenticator 
 ; 
 import 
  
 shopping.merchant.samples.utils.Config 
 ; 
 /** This class demonstrates how to insert a product input */ 
 public 
  
 class 
 InsertProductInputSample 
  
 { 
  
 private 
  
 static 
  
 String 
  
 getParent 
 ( 
 String 
  
 accountId 
 ) 
  
 { 
  
 return 
  
 String 
 . 
 format 
 ( 
 "accounts/%s" 
 , 
  
 accountId 
 ); 
  
 } 
  
 public 
  
 static 
  
 void 
  
 insertProductInput 
 ( 
 Config 
  
 config 
 , 
  
 String 
  
 dataSource 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 // Obtains OAuth token based on the user's configuration. 
  
 GoogleCredentials 
  
 credential 
  
 = 
  
 new 
  
 Authenticator 
 (). 
 authenticate 
 (); 
  
 // Creates service settings using the credentials retrieved above. 
  
 ProductInputsServiceSettings 
  
 productInputsServiceSettings 
  
 = 
  
 ProductInputsServiceSettings 
 . 
 newBuilder 
 () 
  
 . 
 setCredentialsProvider 
 ( 
 FixedCredentialsProvider 
 . 
 create 
 ( 
 credential 
 )) 
  
 . 
 build 
 (); 
  
 // Creates parent to identify where to insert the product. 
  
 String 
  
 parent 
  
 = 
  
 getParent 
 ( 
 config 
 . 
 getAccountId 
 (). 
 toString 
 ()); 
  
 // Calls the API and catches and prints any network failures/errors. 
  
 try 
  
 ( 
 ProductInputsServiceClient 
  
 productInputsServiceClient 
  
 = 
  
 ProductInputsServiceClient 
 . 
 create 
 ( 
 productInputsServiceSettings 
 )) 
  
 { 
  
 // Price to be used for shipping ($33.45). 
  
 Price 
  
 price 
  
 = 
  
 Price 
 . 
 newBuilder 
 (). 
 setAmountMicros 
 ( 
 33_450_000 
 ). 
 setCurrencyCode 
 ( 
 "USD" 
 ). 
 build 
 (); 
  
 Shipping 
  
 shipping 
  
 = 
  
 Shipping 
 . 
 newBuilder 
 () 
  
 . 
 setPrice 
 ( 
 price 
 ) 
  
 . 
 setCountry 
 ( 
 "GB" 
 ) 
  
 . 
 setService 
 ( 
 "1st class post" 
 ) 
  
 . 
 build 
 (); 
  
 Shipping 
  
 shipping2 
  
 = 
  
 Shipping 
 . 
 newBuilder 
 () 
  
 . 
 setPrice 
 ( 
 price 
 ) 
  
 . 
 setCountry 
 ( 
 "FR" 
 ) 
  
 . 
 setService 
 ( 
 "1st class post" 
 ) 
  
 . 
 build 
 (); 
  
 ProductAttributes 
  
 attributes 
  
 = 
  
 ProductAttributes 
 . 
 newBuilder 
 () 
  
 . 
 setTitle 
 ( 
 "A Tale of Two Cities" 
 ) 
  
 . 
 setDescription 
 ( 
 "A classic novel about the French Revolution" 
 ) 
  
 . 
 setLink 
 ( 
 "https://exampleWebsite.com/tale-of-two-cities.html" 
 ) 
  
 . 
 setImageLink 
 ( 
 "https://exampleWebsite.com/tale-of-two-cities.jpg" 
 ) 
  
 . 
 setAvailability 
 ( 
 Availability 
 . 
 IN_STOCK 
 ) 
  
 . 
 setCondition 
 ( 
 Condition 
 . 
 NEW 
 ) 
  
 . 
 setGoogleProductCategory 
 ( 
 "Media > Books" 
 ) 
  
 . 
 addGtins 
 ( 
 "9780007350896" 
 ) 
  
 . 
 addShipping 
 ( 
 shipping 
 ) 
  
 . 
 addShipping 
 ( 
 shipping2 
 ) 
  
 . 
 build 
 (); 
  
 // The datasource can be either a primary or supplemental datasource. 
  
 InsertProductInputRequest 
  
 request 
  
 = 
  
 InsertProductInputRequest 
 . 
 newBuilder 
 () 
  
 . 
 setParent 
 ( 
 parent 
 ) 
  
 // You can only insert products into datasource types of Input "API" and "FILE", and 
  
 // of Type "Primary" or "Supplemental." 
  
 // This field takes the `name` field of the datasource. 
  
 . 
 setDataSource 
 ( 
 dataSource 
 ) 
  
 // If this product is already owned by another datasource, when re-inserting, the 
  
 // new datasource will take ownership of the product. 
  
 . 
 setProductInput 
 ( 
  
 ProductInput 
 . 
 newBuilder 
 () 
  
 . 
 setContentLanguage 
 ( 
 "en" 
 ) 
  
 . 
 setFeedLabel 
 ( 
 "label" 
 ) 
  
 . 
 setOfferId 
 ( 
 "sku123" 
 ) 
  
 . 
 setProductAttributes 
 ( 
 attributes 
 ) 
  
 . 
 build 
 ()) 
  
 . 
 build 
 (); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Sending insert ProductInput request" 
 ); 
  
 ProductInput 
  
 response 
  
 = 
  
 productInputsServiceClient 
 . 
 insertProductInput 
 ( 
 request 
 ); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Inserted ProductInput Name below" 
 ); 
  
 // The last part of the product name will be the product ID assigned to a product by Google. 
  
 // Product ID has the format `contentLanguage~feedLabel~offerId` 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 response 
 . 
 getName 
 ()); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Inserted Product Name below" 
 ); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 response 
 . 
 getProduct 
 ()); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 e 
 ); 
  
 } 
  
 } 
  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 Config 
  
 config 
  
 = 
  
 Config 
 . 
 load 
 (); 
  
 // Identifies the data source that will own the product input. 
  
 String 
  
 dataSource 
  
 = 
  
 "accounts/" 
  
 + 
  
 config 
 . 
 getAccountId 
 () 
  
 + 
  
 "/dataSources/{INSERT_DATASOURCE_ID}" 
 ; 
  
 insertProductInput 
 ( 
 config 
 , 
  
 dataSource 
 ); 
  
 } 
 } 
  
 

PHP

  use Google\ApiCore\ApiException; 
 use Google\Shopping\Merchant\Products\V1\Availability; 
 use Google\Shopping\Merchant\Products\V1\Condition; 
 use Google\Shopping\Merchant\Products\V1\ProductAttributes; 
 use Google\Shopping\Merchant\Products\V1\InsertProductInputRequest; 
 use Google\Shopping\Merchant\Products\V1\ProductInput; 
 use Google\Shopping\Merchant\Products\V1\Client\ProductInputsServiceClient; 
 use Google\Shopping\Merchant\Products\V1\Shipping; 
 use Google\Shopping\Type\Price; 
 /** 
 * Uploads a product input to your Merchant Center account. 
 */ 
 class InsertProductInput 
 { 
 // ENSURE you fill in the datasource ID for the sample to work. 
 private const DATASOURCE = 'INSERT_DATASOURCE_ID'; 
 /** 
 * A helper function to create the parent string. 
 * 
 * @param array $accountId 
 *      The account that owns the product. 
 * 
 * @return string The parent has the format: `accounts/{account_id}` 
 */ 
 private static function getParent($accountId) 
 { 
 return sprintf("accounts/%s", $accountId); 
 } 
 /** 
 * Uploads a product input to your Merchant Center account. If an input 
 * with the same feedLabel, contentLanguage, offerId, and dataSource 
 * already exists, this method replaces that entry. 
 * 
 * After inserting, updating, or deleting a product input, it may take several 
 * minutes before the processed product can be retrieved. 
 * 
 * @param array $config 
 *      The configuration data used for authentication and getting the acccount 
 *      ID. 
 * @param string $dataSource 
 *      The primary or supplemental product data source name. If the 
 *      product already exists and data source provided is different, then the 
 *      product will be moved to a new data source. 
 *      Format: `accounts/{account}/dataSources/{datasource}`. 
 * 
 * @return void 
 */ 
 public static function insertProductInputSample($config, $dataSource): void 
 { 
 // Gets the OAuth credentials to make the request. 
 $credentials = Authentication::useServiceAccountOrTokenFile(); 
 // Creates options config containing credentials for the client to use. 
 $options = ['credentials' => $credentials]; 
 // Creates a client. 
 $productInputsServiceClient = new ProductInputsServiceClient($options); 
 // Creates parent to identify where to insert the product. 
 $parent = self::getParent($config['accountId']); 
 // Calls the API and catches and prints any network failures/errors. 
 try { 
 // Price to be used for shipping ($33.45). 
 $price = new Price( 
 [ 
 'amount_micros' => 33450000, 
 'currency_code' => 'USD' 
 ] 
 ); 
 $shipping = new Shipping( 
 [ 
 'price' => $price, 
 'country' => 'GB', 
 'service' => '1st class post' 
 ] 
 ); 
 $shipping2 = new Shipping( 
 [ 
 'price' => $price, 
 'country' => 'FR', 
 'service' => '1st class post' 
 ] 
 ); 
 // Creates the attributes of the product. 
 $attributes = new ProductAttributes( 
 [ 
 'title' => 'A Tale of Two Cities', 
 'description' => 'A classic novel about the French Revolution', 
 'link' => 'https://exampleWebsite.com/tale-of-two-cities.html', 
 'image_link' = 
> 'https://exampleWebsite.com/tale-of-two-cities.jpg', 
 'availability' => Availability::IN_STOCK, 
 'condition' => Condition::PBNEW, 
 'google_product_category' => 'Media > Books', 
 'gtins' => ['9780007350896'], 
 'shipping' => [$shipping, $shipping2] 
 ] 
 ); 
 // Creates the productInput with the fundamental identifiers. 
 $productInput = new ProductInput( 
 [ 
 'content_language' => 'en', 
 'feed_label' => 'label', 
 'offer_id' => 'sku123ABCD', 
 'product_attributes' => $attributes 
 ] 
 ); 
 // Prepares the request message. 
 $request = new InsertProductInputRequest( 
 [ 
 'parent' => $parent, 
 'data_source' => $dataSource, 
 'product_input' => $productInput 
 ] 
 ); 
 print "Sending insert ProductInput request\n"; 
 $response = $productInputsServiceClient->insertProductInput($request); 
 print "Inserted ProductInput Name below\n"; 
 print $response->getName() . "\n"; 
 print "Inserted Product Name below\n"; 
 print $response->getProduct() . "\n"; 
 } catch (ApiException $e) { 
 print $e->getMessage(); 
 } 
 } 
 /** 
 * Helper to execute the sample. 
 * 
 * @return void 
 */ 
 public function callSample(): void 
 { 
 $config = Config::generateConfig(); 
 // Identifies the data source that will own the product input. 
 $dataSource = sprintf( 
 "accounts/%s/dataSources/%s", 
 $config['accountId'], 
 self::DATASOURCE 
 ); 
 // Makes the call to insert a product to the MC account. 
 self::insertProductInputSample($config, $dataSource); 
 } 
 } 
 // Run the script 
 $sample = new InsertProductInput(); 
 $sample->callSample();  
 
 

Python

  from 
  
 examples.authentication 
  
 import 
 configuration 
 from 
  
 examples.authentication 
  
 import 
 generate_user_credentials 
 from 
  
 google.shopping 
  
 import 
 merchant_products_v1 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 Availability 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 Condition 
 from 
  
 google.shopping.type 
  
 import 
 Price 
 _ACCOUNT 
 = 
 configuration 
 . 
 Configuration 
 () 
 . 
 read_merchant_info 
 () 
 _PARENT 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT 
 } 
 " 
 # You can only insert products into datasource types of Input "API" and 
 # "FILE", and of Type "Primary" or "Supplemental." 
 _DATA_SOURCE 
 = 
 "[INSERT_DATA_SOURCE_HERE]" 
 _DATA_SOURCE_NAME 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT 
 } 
 /dataSources/ 
 { 
 _DATA_SOURCE 
 } 
 " 
 def 
  
 create_product_input 
 (): 
  
 """Creates a `ProductInput` resource.""" 
 # Creates a shipping setting 
 price 
 = 
 Price 
 () 
 price 
 . 
 amount_micros 
 = 
 33_450_000 
 price 
 . 
 currency_code 
 = 
 "GBP" 
 shipping_option_1 
 = 
 merchant_products_v1 
 . 
 Shipping 
 () 
 shipping_option_1 
 . 
 price 
 = 
 price 
 shipping_option_1 
 . 
 country 
 = 
 "GB" 
 shipping_option_1 
 . 
 service 
 = 
 "1st class post" 
 price2 
 = 
 Price 
 () 
 price2 
 . 
 amount_micros 
 = 
 33_450_000 
 price2 
 . 
 currency_code 
 = 
 "EUR" 
 shipping_option_2 
 = 
 merchant_products_v1 
 . 
 Shipping 
 () 
 shipping_option_2 
 . 
 price 
 = 
 price2 
 shipping_option_2 
 . 
 country 
 = 
 "FR" 
 shipping_option_2 
 . 
 service 
 = 
 "2nd class post" 
 # Sets product attributes. Make sure to replace these values with your own. 
 attributes 
 = 
 merchant_products_v1 
 . 
 ProductAttributes 
 () 
 attributes 
 . 
 title 
 = 
 "A Tale of Two Cities" 
 attributes 
 . 
 description 
 = 
 "A classic novel about the French Revolution" 
 attributes 
 . 
 link 
 = 
 "https://exampleWebsite.com/tale-of-two-cities.html" 
 attributes 
 . 
 image_link 
 = 
 "https://exampleWebsite.com/tale-of-two-cities.jpg" 
 attributes 
 . 
 price 
 = 
 price 
 attributes 
 . 
 availability 
 = 
 Availability 
 . 
 IN_STOCK 
 attributes 
 . 
 condition 
 = 
 Condition 
 . 
 NEW 
 attributes 
 . 
 google_product_category 
 = 
 "Media > Books" 
 attributes 
 . 
 gtins 
 = 
 [ 
 "9780007350896" 
 ] 
 attributes 
 . 
 shipping 
 = 
 [ 
 shipping_option_1 
 , 
 shipping_option_2 
 ] 
 return 
 merchant_products_v1 
 . 
 ProductInput 
 ( 
 content_language 
 = 
 "en" 
 , 
 feed_label 
 = 
 "GB" 
 , 
 offer_id 
 = 
 "sku123" 
 , 
 product_attributes 
 = 
 attributes 
 , 
 ) 
 def 
  
 insert_product_input 
 (): 
  
 """Inserts the specified `ProductInput` resource.""" 
 # Gets OAuth Credentials. 
 credentials 
 = 
 generate_user_credentials 
 . 
 main 
 () 
 # Creates a client. 
 client 
 = 
 merchant_products_v1 
 . 
 ProductInputsServiceClient 
 ( 
 credentials 
 = 
 credentials 
 ) 
 # Creates the request. 
 request 
 = 
 merchant_products_v1 
 . 
 InsertProductInputRequest 
 ( 
 parent 
 = 
 _PARENT 
 , 
 # If this product is already owned by another datasource, when 
 # re-inserting, the new datasource will take ownership of the product. 
 product_input 
 = 
 create_product_input 
 (), 
 data_source 
 = 
 _DATA_SOURCE_NAME 
 , 
 ) 
 # Makes the request and catches and prints any error messages. 
 try 
 : 
 response 
 = 
 client 
 . 
 insert_product_input 
 ( 
 request 
 = 
 request 
 ) 
 # The last part of the product name will be the product ID assigned to a 
 # product by Google. Product ID has the format 
 # `contentLanguage~feedLabel~offerId` 
 print 
 ( 
 f 
 "Input successful: 
 { 
 response 
 } 
 " 
 ) 
 except 
 RuntimeError 
 as 
 e 
 : 
 print 
 ( 
 "Input failed" 
 ) 
 print 
 ( 
 e 
 ) 
 # After the product is inserted, the product ID will be returned in the 
 # response. We recommend that you check the Merchant Center to ensure that 
 # the product is approved and visible to users before using the product ID 
 # in any downstream processes. 
 if 
 __name__ 
 == 
 "__main__" 
 : 
 insert_product_input 
 () 
  
 

Apps_Script

  /** 
  
 * 
  
 Inserts 
  
 a 
  
 product 
  
 into 
  
 the 
  
 products 
  
 list 
 . 
  
 Logs 
  
 the 
  
 API 
  
 response 
 . 
  
 */ 
 function 
  
 productInsert 
 () 
  
 { 
  
 // 
  
 IMPORTANT 
 : 
  
 // 
  
 Enable 
  
 the 
  
 Merchant 
  
 API 
  
 Products 
  
 sub 
 - 
 API 
  
 Advanced 
  
 Service 
  
 and 
  
 call 
  
 it 
  
 // 
  
 "MerchantApiProducts" 
  
 // 
  
 Replace 
  
 this 
  
 with 
  
 your 
  
 Merchant 
  
 Center 
  
 ID 
 . 
  
 const 
  
 accountId 
  
 = 
  
 'INSERT_MERCHANT_ID' 
 ; 
  
 // 
  
 Replace 
  
 this 
  
 with 
  
 the 
  
 Data 
  
 Source 
  
 ID 
  
 you 
  
 want 
  
 to 
  
 use 
 . 
  
 const 
  
 dataSourceId 
  
 = 
  
 'INSERT_DATASOURCE_ID' 
 ; 
  
 // 
  
 Construct 
  
 the 
  
 parent 
  
 name 
  
 const 
  
 parent 
  
 = 
  
 'accounts/' 
  
 + 
  
 accountId 
 ; 
  
 // 
  
 Construct 
  
 the 
  
 Data 
  
 Source 
  
 name 
  
 const 
  
 dataSource 
  
 = 
  
 parent 
  
 + 
  
 '/dataSources/' 
  
 + 
  
 dataSourceId 
 ; 
  
 // 
  
 Create 
  
 a 
  
 product 
  
 resource 
  
 and 
  
 insert 
  
 it 
  
 const 
  
 productResource 
  
 = 
  
 { 
  
 'offerId' 
 : 
  
 'fromAppsScript' 
 , 
  
 'contentLanguage' 
 : 
  
 'en' 
 , 
  
 'feedLabel' 
 : 
  
 'US' 
 , 
  
 'productAttributes' 
 : 
  
 { 
  
 'title' 
 : 
  
 'A Tale of Two Cities' 
 , 
  
 'description' 
 : 
  
 'A classic novel about the French Revolution' 
 , 
  
 'link' 
 : 
  
 'http://my-book-shop.com/tale-of-two-cities.html' 
 , 
  
 'imageLink' 
 : 
  
 'http://my-book-shop.com/tale-of-two-cities.jpg' 
 , 
  
 'availability' 
 : 
  
 'in stock' 
 , 
  
 'condition' 
 : 
  
 'new' 
 , 
  
 'googleProductCategory' 
 : 
  
 'Media > Books' 
 , 
  
 'gtin' 
 : 
  
 '[9780007350896]' 
 , 
  
 'price' 
 : 
  
 { 
 'amountMicros' 
 : 
  
 '2500000' 
 , 
  
 'currencyCode' 
 : 
  
 'USD' 
 }, 
  
 } 
  
 }; 
  
 try 
  
 { 
  
 console 
 . 
 log 
 ( 
 'Sending insert ProductInput request' 
 ); 
  
 // 
  
 Call 
  
 the 
  
 ProductInputs 
 . 
 insert 
  
 API 
  
 method 
 . 
  
 response 
  
 = 
  
 MerchantApiProducts 
 . 
 Accounts 
 . 
 ProductInputs 
 . 
 insert 
 ( 
  
 productResource 
 , 
  
 parent 
 , 
  
 { 
 dataSource 
 }); 
  
 // 
  
 RESTful 
  
 insert 
  
 returns 
  
 the 
  
 JSON 
  
 object 
  
 as 
  
 a 
  
 response 
 . 
  
 console 
 . 
 log 
 ( 
 'Inserted ProductInput below' 
 ); 
  
 console 
 . 
 log 
 ( 
 response 
 ); 
  
 } 
  
 catch 
  
 ( 
 e 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 'ERROR!' 
 ); 
  
 console 
 . 
 log 
 ( 
 e 
 ); 
  
 } 
 } 
  
 

cURL

  curl -X POST \ 
 "https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs:insert?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
" \ 
 -H "Authorization: Bearer <API_TOKEN>" \ 
 -H "Content-Type: application/json" \ 
 -d '{ 
 "offerId": "SKU12345", 
 "contentLanguage": "en", 
 "feedLabel": "US", 
 "productAttributes": { 
 "title": "Classic Cotton T-Shirt", 
 "description": "A comfortable, durable, and stylish t-shirt made from 100% cotton.", 
 "link": "https://www.example.com/p/SKU12345", 
 "imageLink": "https://www.example.com/img/SKU12345.jpg", 
 "availability": "IN_STOCK", 
 "price": { 
 "amountMicros": "15990000", 
 "currencyCode": "USD" 
 }, 
 "condition": "NEW", 
 "gtins": ["9780007350896"] 
 } 
 }' 
 

Add or update product information with a supplemental data source

Use a supplemental data source to provide additional or overriding data for products that already exist in a primary data source. This is useful for adding information like promotions, custom labels, or overriding specific product data without modifying the original product input.

To do this, use the productInputs.insert method, but specify the name of your supplemental data source in the dataSource parameter. Verify that offerId , contentLanguage , and feedLabel match an existing product.

This example adds a custom label to an existing product using a supplemental data source.

  POST https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs:insert?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {SUPPLEMENTAL_DATASOURCE_ID} 
 
 
  { 
  
 "offerId" 
 : 
  
 "SKU12345" 
 , 
  
 "contentLanguage" 
 : 
  
 "en" 
 , 
  
 "feedLabel" 
 : 
  
 "US" 
 , 
  
 "productAttributes" 
 : 
  
 { 
  
 "customLabel0" 
 : 
  
 "clearance-sale" 
  
 } 
 } 
 

After this call, the final Product will have customLabel0 set to clearance-sale , assuming the supplemental data source has a higher priority than the primary data source in your feed rules as defined by the default_rule .

Add products asynchronously

To upload a large number of products efficiently, you can send multiple insert requests concurrently. Instead of waiting for a response to each request before sending the next, you can send them asynchronously and handle the responses as they arrive. This approach can significantly improve your data upload throughput.

The following code samples show how to insert products asynchronously. To learn about performing multiple requests at once using curl , see Send multiple requests at once .

Java

  import 
  
 com.google.api.core.ApiFuture 
 ; 
 import 
  
 com.google.api.core.ApiFutureCallback 
 ; 
 import 
  
 com.google.api.core.ApiFutures 
 ; 
 import 
  
 com.google.api.gax.core.FixedCredentialsProvider 
 ; 
 import 
  
 com.google.auth.oauth2.GoogleCredentials 
 ; 
 import 
  
 com.google.common.util.concurrent.MoreExecutors 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Availability 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Condition 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.InsertProductInputRequest 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductAttributes 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInput 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputsServiceClient 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputsServiceSettings 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Shipping 
 ; 
 import 
  
 com.google.shopping.type.Price 
 ; 
 import 
  
 java.util.ArrayList 
 ; 
 import 
  
 java.util.List 
 ; 
 import 
  
 java.util.Random 
 ; 
 import 
  
 java.util.stream.Collectors 
 ; 
 import 
  
 shopping.merchant.samples.utils.Authenticator 
 ; 
 import 
  
 shopping.merchant.samples.utils.Config 
 ; 
 /** This class demonstrates how to insert a product input */ 
 public 
  
 class 
 InsertProductInputAsyncSample 
  
 { 
  
 private 
  
 static 
  
 String 
  
 getParent 
 ( 
 String 
  
 accountId 
 ) 
  
 { 
  
 return 
  
 String 
 . 
 format 
 ( 
 "accounts/%s" 
 , 
  
 accountId 
 ); 
  
 } 
  
 private 
  
 static 
  
 String 
  
 generateRandomString 
 () 
  
 { 
  
 String 
  
 characters 
  
 = 
  
 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 
 ; 
  
 Random 
  
 random 
  
 = 
  
 new 
  
 Random 
 (); 
  
 StringBuilder 
  
 sb 
  
 = 
  
 new 
  
 StringBuilder 
 ( 
 8 
 ); 
  
 for 
  
 ( 
 int 
  
 i 
  
 = 
  
 0 
 ; 
  
 i 
 < 
 8 
 ; 
  
 i 
 ++ 
 ) 
  
 { 
  
 sb 
 . 
 append 
 ( 
 characters 
 . 
 charAt 
 ( 
 random 
 . 
 nextInt 
 ( 
 characters 
 . 
 length 
 ()))); 
  
 } 
  
 return 
  
 sb 
 . 
 toString 
 (); 
  
 } 
  
 private 
  
 static 
  
 ProductInput 
  
 createRandomProduct 
 () 
  
 { 
  
 Price 
  
 price 
  
 = 
  
 Price 
 . 
 newBuilder 
 (). 
 setAmountMicros 
 ( 
 33_450_000 
 ). 
 setCurrencyCode 
 ( 
 "USD" 
 ). 
 build 
 (); 
  
 Shipping 
  
 shipping 
  
 = 
  
 Shipping 
 . 
 newBuilder 
 (). 
 setPrice 
 ( 
 price 
 ). 
 setCountry 
 ( 
 "GB" 
 ). 
 setService 
 ( 
 "1st class post" 
 ). 
 build 
 (); 
  
 Shipping 
  
 shipping2 
  
 = 
  
 Shipping 
 . 
 newBuilder 
 (). 
 setPrice 
 ( 
 price 
 ). 
 setCountry 
 ( 
 "FR" 
 ). 
 setService 
 ( 
 "1st class post" 
 ). 
 build 
 (); 
  
 ProductAttributes 
  
 attributes 
  
 = 
  
 ProductAttributes 
 . 
 newBuilder 
 () 
  
 . 
 setTitle 
 ( 
 "A Tale of Two Cities" 
 ) 
  
 . 
 setDescription 
 ( 
 "A classic novel about the French Revolution" 
 ) 
  
 . 
 setLink 
 ( 
 "https://exampleWebsite.com/tale-of-two-cities.html" 
 ) 
  
 . 
 setImageLink 
 ( 
 "https://exampleWebsite.com/tale-of-two-cities.jpg" 
 ) 
  
 . 
 setAvailability 
 ( 
 Availability 
 . 
 IN_STOCK 
 ) 
  
 . 
 setCondition 
 ( 
 Condition 
 . 
 NEW 
 ) 
  
 . 
 setGoogleProductCategory 
 ( 
 "Media > Books" 
 ) 
  
 . 
 addGtins 
 ( 
 "9780007350896" 
 ) 
  
 . 
 addShipping 
 ( 
 shipping 
 ) 
  
 . 
 addShipping 
 ( 
 shipping2 
 ) 
  
 . 
 build 
 (); 
  
 return 
  
 ProductInput 
 . 
 newBuilder 
 () 
  
 . 
 setContentLanguage 
 ( 
 "en" 
 ) 
  
 . 
 setFeedLabel 
 ( 
 "CH" 
 ) 
  
 . 
 setOfferId 
 ( 
 generateRandomString 
 ()) 
  
 . 
 setProductAttributes 
 ( 
 attributes 
 ) 
  
 . 
 build 
 (); 
  
 } 
  
 public 
  
 static 
  
 void 
  
 asyncInsertProductInput 
 ( 
 Config 
  
 config 
 , 
  
 String 
  
 dataSource 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 // Obtains OAuth token based on the user's configuration. 
  
 GoogleCredentials 
  
 credential 
  
 = 
  
 new 
  
 Authenticator 
 (). 
 authenticate 
 (); 
  
 // Creates service settings using the credentials retrieved above. 
  
 ProductInputsServiceSettings 
  
 productInputsServiceSettings 
  
 = 
  
 ProductInputsServiceSettings 
 . 
 newBuilder 
 () 
  
 . 
 setCredentialsProvider 
 ( 
 FixedCredentialsProvider 
 . 
 create 
 ( 
 credential 
 )) 
  
 . 
 build 
 (); 
  
 // Creates parent to identify where to insert the product. 
  
 String 
  
 parent 
  
 = 
  
 getParent 
 ( 
 config 
 . 
 getAccountId 
 (). 
 toString 
 ()); 
  
 // Calls the API and catches and prints any network failures/errors. 
  
 try 
  
 ( 
 ProductInputsServiceClient 
  
 productInputsServiceClient 
  
 = 
  
 ProductInputsServiceClient 
 . 
 create 
 ( 
 productInputsServiceSettings 
 )) 
  
 { 
  
 // Creates five insert product input requests with random product IDs. 
  
 List<InsertProductInputRequest> 
  
 requests 
  
 = 
  
 new 
  
 ArrayList 
<> ( 
 5 
 ); 
  
 for 
  
 ( 
 int 
  
 i 
  
 = 
  
 0 
 ; 
  
 i 
 < 
 5 
 ; 
  
 i 
 ++ 
 ) 
  
 { 
  
 InsertProductInputRequest 
  
 request 
  
 = 
  
 InsertProductInputRequest 
 . 
 newBuilder 
 () 
  
 . 
 setParent 
 ( 
 parent 
 ) 
  
 // You can only insert products into datasource types of Input "API", and of Type 
  
 // "Primary" or "Supplemental." 
  
 // This field takes the `name` field of the datasource. 
  
 . 
 setDataSource 
 ( 
 dataSource 
 ) 
  
 // If this product is already owned by another datasource, when re-inserting, the 
  
 // new datasource will take ownership of the product. 
  
 . 
 setProductInput 
 ( 
 createRandomProduct 
 ()) 
  
 . 
 build 
 (); 
  
 requests 
 . 
 add 
 ( 
 request 
 ); 
  
 } 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Sending insert product input requests" 
 ); 
  
 List<ApiFuture<ProductInput> 
>  
 futures 
  
 = 
  
 requests 
 . 
 stream 
 () 
  
 . 
 map 
 ( 
  
 request 
  
 - 
>  
 productInputsServiceClient 
 . 
 insertProductInputCallable 
 (). 
 futureCall 
 ( 
 request 
 )) 
  
 . 
 collect 
 ( 
 Collectors 
 . 
 toList 
 ()); 
  
 // Creates callback to handle the responses when all are ready. 
  
 ApiFuture<List<ProductInput> 
>  
 responses 
  
 = 
  
 ApiFutures 
 . 
 allAsList 
 ( 
 futures 
 ); 
  
 ApiFutures 
 . 
 addCallback 
 ( 
  
 responses 
 , 
  
 new 
  
 ApiFutureCallback<List<ProductInput> 
> () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onSuccess 
 ( 
 List<ProductInput> 
  
 results 
 ) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Inserted products below" 
 ); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 results 
 ); 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onFailure 
 ( 
 Throwable 
  
 throwable 
 ) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 throwable 
 ); 
  
 } 
  
 }, 
  
 MoreExecutors 
 . 
 directExecutor 
 ()); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 e 
 ); 
  
 } 
  
 } 
  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 Config 
  
 config 
  
 = 
  
 Config 
 . 
 load 
 (); 
  
 // Identifies the data source that will own the product input. 
  
 String 
  
 dataSource 
  
 = 
  
 "accounts/" 
  
 + 
  
 config 
 . 
 getAccountId 
 () 
  
 + 
  
 "/dataSources/{datasourceId}" 
 ; 
  
 asyncInsertProductInput 
 ( 
 config 
 , 
  
 dataSource 
 ); 
  
 } 
 } 
  
 

Python

  import 
  
 asyncio 
 import 
  
 functools 
 import 
  
 random 
 import 
  
 string 
 from 
  
 examples.authentication 
  
 import 
 configuration 
 from 
  
 examples.authentication 
  
 import 
 generate_user_credentials 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 Availability 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 Condition 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 InsertProductInputRequest 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 ProductAttributes 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 ProductInput 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 ProductInputsServiceAsyncClient 
 from 
  
 google.shopping.merchant_products_v1 
  
 import 
 Shipping 
 from 
  
 google.shopping.type 
  
 import 
 Price 
 # Read merchant account information from the configuration file. 
 _ACCOUNT_ID 
 = 
 configuration 
 . 
 Configuration 
 () 
 . 
 read_merchant_info 
 () 
 # The parent account for the product input. 
 # Format: accounts/{account} 
 _PARENT 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT_ID 
 } 
 " 
 def 
  
 _generate_random_string 
 ( 
 length 
 : 
 int 
 = 
 8 
 ) 
 - 
> str 
 : 
  
 """Generates a random string of a given length.""" 
 characters 
 = 
 string 
 . 
 ascii_letters 
 + 
 string 
 . 
 digits 
 return 
 "" 
 . 
 join 
 ( 
 random 
 . 
 choice 
 ( 
 characters 
 ) 
 for 
 _ 
 in 
 range 
 ( 
 length 
 )) 
 def 
  
 _create_random_product 
 () 
 - 
> ProductInput 
 : 
  
 """Creates a ProductInput with random elements and predefined attributes.""" 
 price 
 = 
 Price 
 ( 
 amount_micros 
 = 
 33450000 
 , 
 currency_code 
 = 
 "USD" 
 ) 
 shipping1 
 = 
 Shipping 
 ( 
 price 
 = 
 price 
 , 
 country 
 = 
 "GB" 
 , 
 service 
 = 
 "1st class post" 
 ) 
 shipping2 
 = 
 Shipping 
 ( 
 price 
 = 
 price 
 , 
 country 
 = 
 "FR" 
 , 
 service 
 = 
 "1st class post" 
 ) 
 attributes 
 = 
 ProductAttributes 
 ( 
 title 
 = 
 "Async - A Tale of Two Cities" 
 , 
 description 
 = 
 "A classic novel about the French Revolution" 
 , 
 link 
 = 
 "https://exampleWebsite.com/tale-of-two-cities.html" 
 , 
 image_link 
 = 
 "https://exampleWebsite.com/tale-of-two-cities.jpg" 
 , 
 availability 
 = 
 Availability 
 . 
 IN_STOCK 
 , 
 condition 
 = 
 Condition 
 . 
 NEW 
 , 
 google_product_category 
 = 
 "Media > Books" 
 , 
 gtins 
 = 
 [ 
 "9780007350896" 
 ], 
 shipping 
 = 
 [ 
 shipping1 
 , 
 shipping2 
 ], 
 ) 
 return 
 ProductInput 
 ( 
 content_language 
 = 
 "en" 
 , 
 feed_label 
 = 
 "US" 
 , 
 offer_id 
 = 
 _generate_random_string 
 (), 
 product_attributes 
 = 
 attributes 
 , 
 ) 
 def 
  
 print_product_input 
 ( 
 i 
 , 
 task 
 ): 
 print 
 ( 
 "Inserted ProductInput number: " 
 , 
 i 
 ) 
 # task.result() contains the response from async_insert_product_input 
 print 
 ( 
 task 
 . 
 result 
 ()) 
 async 
 def 
  
 async_insert_product_input 
 ( 
 client 
 : 
 ProductInputsServiceAsyncClient 
 , 
 request 
 : 
 InsertProductInputRequest 
 ): 
  
 """Inserts product inputs. 
 Args: 
 client: The ProductInputsServiceAsyncClient to use. 
 request: The InsertProductInputRequest to send. 
 Returns: 
 The response from the insert_produc_input request. 
 """ 
 print 
 ( 
 "Sending insert product input requests" 
 ) 
 try 
 : 
 response 
 = 
 await 
 client 
 . 
 insert_product_input 
 ( 
 request 
 = 
 request 
 ) 
 # The response is an async corouting inserting the ProductInput. 
 return 
 response 
 except 
 RuntimeError 
 as 
 e 
 : 
 # Catch and print any exceptions that occur during the API calls. 
 print 
 ( 
 e 
 ) 
 async 
 def 
  
 main 
 (): 
 # The ID of the data source that will own the product input. 
 # This is a placeholder and should be replaced with an actual data source ID. 
 datasource_id 
 = 
 "<INSERT_DATA_SOURCE_ID_HERE>" 
 data_source_name 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT_ID 
 } 
 /dataSources/ 
 { 
 datasource_id 
 } 
 " 
 # Gets OAuth Credentials. 
 credentials 
 = 
 generate_user_credentials 
 . 
 main 
 () 
 # Creates a ProductInputsServiceClient. 
 client 
 = 
 ProductInputsServiceAsyncClient 
 ( 
 credentials 
 = 
 credentials 
 ) 
 tasks 
 = 
 [] 
 for 
 i 
 in 
 range 
 ( 
 5 
 ): 
 product_input 
 = 
 _create_random_product 
 () 
 request 
 = 
 InsertProductInputRequest 
 ( 
 parent 
 = 
 _PARENT 
 , 
 data_source 
 = 
 data_source_name 
 , 
 product_input 
 = 
 product_input 
 , 
 ) 
 # Create the async task 
 insert_product_task 
 = 
 asyncio 
 . 
 create_task 
 ( 
 async_insert_product_input 
 ( 
 client 
 , 
 request 
 ) 
 ) 
 # Add the callback 
 callback_function 
 = 
 functools 
 . 
 partial 
 ( 
 print_product_input 
 , 
 i 
 ) 
 insert_product_task 
 . 
 add_done_callback 
 ( 
 callback_function 
 ) 
 # Add the task to our list 
 tasks 
 . 
 append 
 ( 
 insert_product_task 
 ) 
 # Await all tasks to complete concurrently 
 # The print_product_input callback will be called for each as it finishes 
 await 
 asyncio 
 . 
 gather 
 ( 
 * 
 tasks 
 ) 
 if 
 __name__ 
 == 
 "__main__" 
 : 
 asyncio 
 . 
 run 
 ( 
 main 
 ()) 
  
 

Add products targeting specific countries

You can set country targeting for individual products to override the settings from the data source, or to target products when the data source has no countries defined.

Target countries with the shipping attribute

If your data source doesn't have specific countries defined, you can target countries for a product by providing the shipping field within productAttributes . This field lets you specify shipping costs and rules for different countries, implicitly targeting them for serving.

This example targets a product to the US and Canada by providing two separate shipping entries.

  POST https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs:insert?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
 
 
  { 
  
 "offerId" 
 : 
  
 "SKU_SHIP_TARGET" 
 , 
  
 "contentLanguage" 
 : 
  
 "en" 
 , 
  
 "feedLabel" 
 : 
  
 "GLOBAL" 
 , 
  
 "productAttributes" 
 : 
  
 { 
  
 "title" 
 : 
  
 "Global T-Shirt" 
 , 
  
 "link" 
 : 
  
 "https://www.example.com/p/SKU_SHIP_TARGET" 
 , 
  
 "imageLink" 
 : 
  
 "https://www.example.com/img/SKU_SHIP_TARGET.jpg" 
 , 
  
 "availability" 
 : 
  
 "IN_STOCK" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "25990000" 
 , 
  
 "currencyCode" 
 : 
  
 "USD" 
  
 }, 
  
 "shipping" 
 : 
  
 [ 
  
 { 
  
 "country" 
 : 
  
 "US" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "5990000" 
 , 
  
 "currencyCode" 
 : 
  
 "USD" 
  
 } 
  
 }, 
  
 { 
  
 "country" 
 : 
  
 "CA" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "10990000" 
 , 
  
 "currencyCode" 
 : 
  
 "CAD" 
  
 } 
  
 } 
  
 ] 
  
 } 
 } 
 

Exclude countries and destinations

If your data source already targets multiple countries, you can exclude a product from showing in a subset of those countries or from specific destinations.

  • Use the shoppingAdsExcludedCountries attribute to prevent a product from appearing in Shopping ads in specific countries.
  • Use the excludedDestinations attribute to prevent a product from appearing within specific Marketing Methods , such as FreeListings .

This example assumes the data source targets the US, Canada, and Mexico. The request excludes the product from Shopping ads in Mexico and from the Free Listings destination entirely.

  POST https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs:insert?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
 
 
  { 
  
 "offerId" 
 : 
  
 "SKU_EXCLUDE_TARGET" 
 , 
  
 "contentLanguage" 
 : 
  
 "en" 
 , 
  
 "feedLabel" 
 : 
  
 "NA" 
 , 
  
 "productAttributes" 
 : 
  
 { 
  
 "title" 
 : 
  
 "North America T-Shirt" 
 , 
  
 "link" 
 : 
  
 "https://www.example.com/p/SKU_EXCLUDE_TARGET" 
 , 
  
 "imageLink" 
 : 
  
 "https://www.example.com/img/SKU_EXCLUDE_TARGET.jpg" 
 , 
  
 "availability" 
 : 
  
 "IN_STOCK" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "19990000" 
 , 
  
 "currencyCode" 
 : 
  
 "USD" 
  
 }, 
  
 "shoppingAdsExcludedCountries" 
 : 
  
 [ 
 "MX" 
 ], 
  
 "excludedDestinations" 
 : 
  
 [ 
 "FREE_LISTINGS" 
 ] 
  
 } 
 } 
 

Get product information

To retrieve the final processed state of a product, use the products.get method. This method returns the Product resource, which includes all productAttributes after feed rules and supplemental data sources have been applied, along with its current validation status.

There can be a delay of a few minutes between when you insert or update a ProductInput and when the final Product is available to retrieve.

  GET https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/products/en~US~SKU12345 
 

A successful request returns the Product resource.

  { 
  
 "name" 
 : 
  
 "accounts/ {ACCOUNT_ID} 
/products/en~US~SKU12345" 
 , 
  
 "offerId" 
 : 
  
 "SKU12345" 
 , 
  
 "contentLanguage" 
 : 
  
 "en" 
 , 
  
 "feedLabel" 
 : 
  
 "US" 
 , 
  
 "dataSource" 
 : 
  
 "accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
" 
 , 
  
 "productAttributes" 
 : 
  
 { 
  
 "title" 
 : 
  
 "Classic Cotton T-Shirt" 
 , 
  
 "description" 
 : 
  
 "A comfortable, durable, and stylish t-shirt made from 100% cotton." 
 , 
  
 "link" 
 : 
  
 "https://www.example.com/p/SKU12345" 
 , 
  
 "imageLink" 
 : 
  
 "https://www.example.com/img/SKU12345.jpg" 
 , 
  
 "availability" 
 : 
  
 "IN_STOCK" 
 , 
  
 "price" 
 : 
  
 { 
  
 "amountMicros" 
 : 
  
 "15990000" 
 , 
  
 "currencyCode" 
 : 
  
 "USD" 
  
 }, 
  
 "condition" 
 : 
  
 "NEW" 
 , 
  
 "gtins" 
 : 
  
 [ 
  
 "9780007350896" 
  
 ] 
  
 }, 
  
 "productStatus" 
 : 
  
 { 
  
 "destinationStatuses" 
 : 
  
 [ 
  
 { 
  
 "reportingContext" 
 : 
  
 "SHOPPING_ADS" 
 , 
  
 "approvedCountries" 
 : 
  
 [ 
  
 "US" 
  
 ] 
  
 } 
  
 ], 
  
 "creationDate" 
 : 
  
 "2024-05-20T10:00:00Z" 
 , 
  
 "lastUpdateDate" 
 : 
  
 "2024-05-20T10:05:00Z" 
 , 
  
 "googleExpirationDate" 
 : 
  
 "2024-06-19T10:05:00Z" 
  
 } 
 } 
 

The following code samples show how to get a product.

Java

  import 
  
 com.google.api.gax.core.FixedCredentialsProvider 
 ; 
 import 
  
 com.google.auth.oauth2.GoogleCredentials 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.GetProductRequest 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.Product 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductsServiceClient 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductsServiceSettings 
 ; 
 import 
  
 shopping.merchant.samples.utils.Authenticator 
 ; 
 import 
  
 shopping.merchant.samples.utils.Config 
 ; 
 /** This class demonstrates how to get a single product for a given Merchant Center account */ 
 public 
  
 class 
 GetProductSample 
  
 { 
  
 public 
  
 static 
  
 void 
  
 getProduct 
 ( 
 Config 
  
 config 
 , 
  
 String 
  
 product 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 // Obtains OAuth token based on the user's configuration. 
  
 GoogleCredentials 
  
 credential 
  
 = 
  
 new 
  
 Authenticator 
 (). 
 authenticate 
 (); 
  
 // Creates service settings using the credentials retrieved above. 
  
 ProductsServiceSettings 
  
 productsServiceSettings 
  
 = 
  
 ProductsServiceSettings 
 . 
 newBuilder 
 () 
  
 . 
 setCredentialsProvider 
 ( 
 FixedCredentialsProvider 
 . 
 create 
 ( 
 credential 
 )) 
  
 . 
 build 
 (); 
  
 // Calls the API and catches and prints any network failures/errors. 
  
 try 
  
 ( 
 ProductsServiceClient 
  
 productsServiceClient 
  
 = 
  
 ProductsServiceClient 
 . 
 create 
 ( 
 productsServiceSettings 
 )) 
  
 { 
  
 // The name has the format: accounts/{account}/products/{productId} 
  
 GetProductRequest 
  
 request 
  
 = 
  
 GetProductRequest 
 . 
 newBuilder 
 (). 
 setName 
 ( 
 product 
 ). 
 build 
 (); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Sending get product request:" 
 ); 
  
 Product 
  
 response 
  
 = 
  
 productsServiceClient 
 . 
 getProduct 
 ( 
 request 
 ); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Retrieved Product below" 
 ); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 response 
 ); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 e 
 ); 
  
 } 
  
 } 
  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 Config 
  
 config 
  
 = 
  
 Config 
 . 
 load 
 (); 
  
 // The name of the `product`, returned after a `Product.insert` request. We recommend 
  
 // having stored this value in your database to use for all future requests. 
  
 String 
  
 product 
  
 = 
  
 "accounts/{datasource}/products/{productId}" 
 ; 
  
 getProduct 
 ( 
 config 
 , 
  
 product 
 ); 
  
 } 
 } 
  
 

PHP

  use Google\ApiCore\ApiException; 
 use Google\Shopping\Merchant\Products\V1\GetProductRequest; 
 use Google\Shopping\Merchant\Products\V1\Product; 
 use Google\Shopping\Merchant\Products\V1\Client\ProductsServiceClient; 
 /** 
 * This class demonstrates how to get a single product for a given Merchant Center 
 * account. 
 */ 
 class GetProduct 
 { 
 // ENSURE you fill in the product ID for the sample to work. 
 private const PRODUCT = 'INSERT_PRODUCT_ID_HERE'; 
 /** 
 * Gets a product from your Merchant Center account. 
 * 
 * @param array  $config 
 *      The configuration data used for authentication and getting the acccount 
 *      ID. 
 * @param string $product 
 *      The product ID to get. 
 *      Format: `accounts/{account}/products/{productId}`. 
 * 
 * @return void 
 */ 
 public static function getProductSample($config, $product) : void 
 { 
 // Gets the OAuth credentials to make the request. 
 $credentials = Authentication::useServiceAccountOrTokenFile(); 
 // Creates options config containing credentials for the client to use. 
 $options = ['credentials' => $credentials]; 
 // Creates a client. 
 $productsServiceClient = new ProductsServiceClient($options); 
 try { 
 // Creates the request. 
 $request = new GetProductRequest( 
 [ 
 'name' => $product 
 ] 
 ); 
 // Sends the request. 
 echo "Sending get Product request\n"; 
 $response = $productsServiceClient->getProduct($request); 
 // Outputs the response. 
 echo "Retrieved Product below\n"; 
 print_r($response); 
 } catch (Exception $e) { 
 echo $e->getMessage(); 
 } 
 } 
 /** 
 * Helper to execute the sample. 
 * 
 * @return void 
 */ 
 public function callSample(): void 
 { 
 $config = Config::generateConfig(); 
 // Identifies the product to retrieve. 
 // The name has the format: accounts/{account}/products/{productId}. 
 $product = ProductsServiceClient::productName( 
 $config['accountId'], 
 self::PRODUCT 
 ); 
 self::getProductSample($config, $product); 
 } 
 } 
 $sample = new GetProduct(); 
 $sample->callSample();  
 
 

Python

  from 
  
 examples.authentication 
  
 import 
 configuration 
 from 
  
 examples.authentication 
  
 import 
 generate_user_credentials 
 from 
  
 google.shopping 
  
 import 
 merchant_products_v1 
 _ACCOUNT 
 = 
 configuration 
 . 
 Configuration 
 () 
 . 
 read_merchant_info 
 () 
 # ENSURE you fill in the product ID for the sample to 
 # work. 
 # In the format of `contentLanguage~feedLabel~offerId` 
 _PRODUCT 
 = 
 "[INSERT_PRODUCT_HERE]" 
 _NAME 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT 
 } 
 /products/ 
 { 
 _PRODUCT 
 } 
 " 
 def 
  
 get_product 
 (): 
  
 """Gets the specified `Product` resource.""" 
 # Gets OAuth Credentials. 
 credentials 
 = 
 generate_user_credentials 
 . 
 main 
 () 
 # Creates a client. 
 client 
 = 
 merchant_products_v1 
 . 
 ProductsServiceClient 
 ( 
 credentials 
 = 
 credentials 
 ) 
 # Creates the request. 
 request 
 = 
 merchant_products_v1 
 . 
 GetProductRequest 
 ( 
 name 
 = 
 _NAME 
 ) 
 # Makes the request and catches and prints any error messages. 
 try 
 : 
 response 
 = 
 client 
 . 
 get_product 
 ( 
 request 
 = 
 request 
 ) 
 print 
 ( 
 f 
 "Get successful: 
 { 
 response 
 } 
 " 
 ) 
 except 
 RuntimeError 
 as 
 e 
 : 
 print 
 ( 
 "Get failed" 
 ) 
 print 
 ( 
 e 
 ) 
 if 
 __name__ 
 == 
 "__main__" 
 : 
 get_product 
 () 
  
 

Apps_Script

  /** 
  
 * 
  
 Get 
  
 a 
  
 specific 
  
 product 
  
 for 
  
 a 
  
 given 
  
 Merchant 
  
 Center 
  
 account 
 . 
  
 */ 
 function 
  
 getProduct 
 () 
  
 { 
  
 // 
  
 IMPORTANT 
 : 
  
 // 
  
 Enable 
  
 the 
  
 Merchant 
  
 API 
  
 Products 
  
 sub 
 - 
 API 
  
 Advanced 
  
 Service 
  
 and 
  
 call 
  
 it 
  
 // 
  
 "MerchantApiProducts" 
  
 // 
  
 Replace 
  
 this 
  
 with 
  
 your 
  
 Merchant 
  
 Center 
  
 ID 
 . 
  
 const 
  
 accountId 
  
 = 
  
 '<MERCHANT_CENTER_ID>' 
 ; 
  
 // 
  
 The 
  
 ID 
  
 of 
  
 the 
  
 product 
  
 to 
  
 retrieve 
 . 
  
 // 
  
 This 
  
 ID 
  
 is 
  
 assigned 
  
 by 
  
 Google 
  
 and 
  
 typically 
  
 follows 
  
 the 
  
 format 
 : 
  
 // 
  
 channel 
 ~ 
 contentLanguage 
 ~ 
 feedLabel 
 ~ 
 offerId 
  
 // 
  
 Replace 
  
 with 
  
 an 
  
 actual 
  
 product 
  
 ID 
  
 from 
  
 your 
  
 Merchant 
  
 Center 
  
 account 
 . 
  
 const 
  
 productId 
  
 = 
  
 '<PRODUCT_ID>' 
 ; 
  
 // 
  
 Construct 
  
 the 
  
 parent 
  
 name 
  
 const 
  
 parent 
  
 = 
  
 'accounts/' 
  
 + 
  
 accountId 
 ; 
  
 // 
  
 Construct 
  
 the 
  
 product 
  
 resource 
  
 name 
  
 const 
  
 name 
  
 = 
  
 parent 
  
 + 
  
 "/products/" 
  
 + 
  
 productId 
 ; 
  
 try 
  
 { 
  
 console 
 . 
 log 
 ( 
 'Sending get Product request' 
 ); 
  
 // 
  
 Call 
  
 the 
  
 Products 
 . 
 get 
  
 API 
  
 method 
 . 
  
 product 
  
 = 
  
 MerchantApiProducts 
 . 
 Accounts 
 . 
 Products 
 . 
 get 
 ( 
 name 
 ); 
  
 console 
 . 
 log 
 ( 
 product 
 ); 
  
 } 
  
 catch 
  
 ( 
 e 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 'ERROR!' 
 ); 
  
 console 
 . 
 log 
 ( 
 e 
 ); 
  
 } 
 } 
  
 

cURL

  curl -X GET \ 
 "https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/products/en~US~SKU12345" \ 
 -H "Authorization: Bearer <API_TOKEN>" 
 

Delete a product from a data source

To delete a product input from a specific data source, use the productInputs.delete method.

  • If you delete from a primary data source, the entire product including its supplemental ProductInputs is removed from Merchant Center.
  • If you delete from a supplemental data source, only the attributes from that data source are removed from the product. The product itself remains, sourced from the primary and any other supplemental data sources.

You must specify the dataSource from which you are deleting the product input.

  DELETE https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs/en~US~SKU12345?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
 
 

A successful call returns an empty response.

The following code samples show how to delete a product.

Java

  import 
  
 com.google.api.gax.core.FixedCredentialsProvider 
 ; 
 import 
  
 com.google.auth.oauth2.GoogleCredentials 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.DeleteProductInputRequest 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputName 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputsServiceClient 
 ; 
 import 
  
 com.google.shopping.merchant.products.v1.ProductInputsServiceSettings 
 ; 
 import 
  
 shopping.merchant.samples.utils.Authenticator 
 ; 
 import 
  
 shopping.merchant.samples.utils.Config 
 ; 
 /** This class demonstrates how to delete a product for a given Merchant Center account */ 
 public 
  
 class 
 DeleteProductInputSample 
  
 { 
  
 public 
  
 static 
  
 void 
  
 deleteProductInput 
 ( 
 Config 
  
 config 
 , 
  
 String 
  
 productId 
 , 
  
 String 
  
 dataSource 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 // Obtains OAuth token based on the user's configuration. 
  
 GoogleCredentials 
  
 credential 
  
 = 
  
 new 
  
 Authenticator 
 (). 
 authenticate 
 (); 
  
 // Creates service settings using the credentials retrieved above. 
  
 ProductInputsServiceSettings 
  
 productInputsServiceSettings 
  
 = 
  
 ProductInputsServiceSettings 
 . 
 newBuilder 
 () 
  
 . 
 setCredentialsProvider 
 ( 
 FixedCredentialsProvider 
 . 
 create 
 ( 
 credential 
 )) 
  
 . 
 build 
 (); 
  
 // Creates product name to identify product. 
  
 String 
  
 name 
  
 = 
  
 ProductInputName 
 . 
 newBuilder 
 () 
  
 . 
 setAccount 
 ( 
 config 
 . 
 getAccountId 
 (). 
 toString 
 ()) 
  
 . 
 setProductinput 
 ( 
 productId 
 ) 
  
 . 
 build 
 () 
  
 . 
 toString 
 (); 
  
 // Calls the API and catches and prints any network failures/errors. 
  
 try 
  
 ( 
 ProductInputsServiceClient 
  
 productInputsServiceClient 
  
 = 
  
 ProductInputsServiceClient 
 . 
 create 
 ( 
 productInputsServiceSettings 
 )) 
  
 { 
  
 DeleteProductInputRequest 
  
 request 
  
 = 
  
 DeleteProductInputRequest 
 . 
 newBuilder 
 (). 
 setName 
 ( 
 name 
 ). 
 setDataSource 
 ( 
 dataSource 
 ). 
 build 
 (); 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 "Sending deleteProductInput request" 
 ); 
  
 productInputsServiceClient 
 . 
 deleteProductInput 
 ( 
 request 
 ); 
  
 // no response returned on success 
  
 System 
 . 
 out 
 . 
 println 
 ( 
  
 "Delete successful, note that it may take a few minutes for the delete to update in" 
  
 + 
  
 " the system. If you make a products.get or products.list request before a few" 
  
 + 
  
 " minutes have passed, the old product data may be returned." 
 ); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 System 
 . 
 out 
 . 
 println 
 ( 
 e 
 ); 
  
 } 
  
 } 
  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 Config 
  
 config 
  
 = 
  
 Config 
 . 
 load 
 (); 
  
 // An ID assigned to a product by Google. In the format 
  
 // contentLanguage~feedLabel~offerId 
  
 String 
  
 productId 
  
 = 
  
 "online~en~label~sku123" 
 ; 
  
 // The name of the dataSource from which to delete the product. If it is a primary feed, this 
  
 // will delete the product completely. If it's a supplemental feed, it will only delete the 
  
 // product information from that feed, but the product will still be available from the primary 
  
 // feed. 
  
 String 
  
 dataSource 
  
 = 
  
 "accounts/{account}/dataSources/{dataSource}" 
 ; 
  
 deleteProductInput 
 ( 
 config 
 , 
  
 productId 
 , 
  
 dataSource 
 ); 
  
 } 
 } 
  
 

PHP

  use Google\ApiCore\ApiException; 
 use Google\Auth\CredentialsLoader; 
 use Google\Shopping\Merchant\Products\V1\Client\ProductInputsServiceClient; 
 use Google\Shopping\Merchant\Products\V1\DeleteProductInputRequest; 
 use Google\Shopping\Merchant\Products\V1\ProductInputName; 
 /** 
 * This class demonstrates how to delete a productinput for a given Merchant Center 
 * account. 
 */ 
 class DeleteProductInput 
 { 
 // ENSURE you fill in the product and datasource ID for the sample to work. 
 // An ID assigned to a product by Google: 
 // In the format `contentLanguage~feedLabel~offerId` 
 private const PRODUCT = 'INSERT_PRODUCT_ID_HERE'; 
 private const DATASOURCE = 'INSERT_DATASOURCE_ID_HERE'; 
 /** 
 * Deletes a product input to your Merchant Center account. 
 * 
 * @param array  $config 
 *      The configuration data used for authentication and getting the 
 *      acccount ID. 
 * @param string $product 
 *      The product ID to delete. 
 *      Format: `accounts/{account}/products/{productId}`. 
 * @param string $dataSource 
 *      The primary or supplemental product data source name that owns the 
 *      product. 
 *      Format: `accounts/{account}/dataSources/{datasource}`. 
 * 
 * @return void 
 */ 
 public static function deleteProductInputSample( 
 $config, $product, $dataSource 
 ): void { 
 // Obtains OAuth token based on the user's configuration. 
 $credentials = Authentication::useServiceAccountOrTokenFile(); 
 // Creates service settings using the credentials retrieved above. 
 $options = ['credentials' => $credentials]; 
 $productInputsServiceClient = new ProductInputsServiceClient($options); 
 // Calls the API and catches and prints any network failures/errors. 
 try { 
 $request = new DeleteProductInputRequest( 
 [ 
 'name' => $product, 
 'data_source' => $dataSource 
 ] 
 ); 
 echo "Sending deleteProductInput request\n"; 
 $productInputsServiceClient->deleteProductInput($request); 
 echo "Delete successful, note that it may take a few minutes for the " 
 . "delete to update in the system. If you make a products.get or " 
 . "products.list request before a few  minutes have passed, the old " 
 . "product data may be returned.\n"; 
 } catch (ApiException $e) { 
 echo "An error has occurred: \n"; 
 echo $e->getMessage() . "\n"; 
 } 
 } 
 /** 
 * Helper to execute the sample. 
 * 
 * @return void 
 */ 
 public function callSample(): void 
 { 
 $config = Config::generateConfig(); 
 // The productID variable is defined at the top of the file. 
 $product = ProductInputsServiceClient::productInputName( 
 $config['accountId'], 
 self::PRODUCT 
 ); 
 // The name of the dataSource from which to delete the product. 
 $dataSource = sprintf( 
 'accounts/%s/dataSources/%s', 
 $config['accountId'], 
 self::DATASOURCE 
 ); 
 self::deleteProductInputSample($config, $product, $dataSource); 
 } 
 } 
 // Run the script. 
 $sample = new DeleteProductInput(); 
 $sample->callSample();  
 
 

Python

  from 
  
 examples.authentication 
  
 import 
 configuration 
 from 
  
 examples.authentication 
  
 import 
 generate_user_credentials 
 from 
  
 google.shopping 
  
 import 
 merchant_products_v1 
 _ACCOUNT 
 = 
 configuration 
 . 
 Configuration 
 () 
 . 
 read_merchant_info 
 () 
 # ENSURE you fill in the product ID and data source for the 
 # sample to work. 
 # In the format of `contentLanguage~feedLabel~offerId` 
 _PRODUCT 
 = 
 "[INSERT_PRODUCT_HERE]" 
 _DATA_SOURCE 
 = 
 "[INSERT_DATA_SOURCE_HERE]" 
 _NAME 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT 
 } 
 /productInputs/ 
 { 
 _PRODUCT 
 } 
 " 
 _DATA_SOURCE_NAME 
 = 
 f 
 "accounts/ 
 { 
 _ACCOUNT 
 } 
 /dataSources/ 
 { 
 _DATA_SOURCE 
 } 
 " 
 def 
  
 delete_product_input 
 (): 
  
 """Deletes the specified `ProductInput` resource.""" 
 # Gets OAuth Credentials. 
 credentials 
 = 
 generate_user_credentials 
 . 
 main 
 () 
 # Creates a client. 
 client 
 = 
 merchant_products_v1 
 . 
 ProductInputsServiceClient 
 ( 
 credentials 
 = 
 credentials 
 ) 
 # Creates the request. 
 request 
 = 
 merchant_products_v1 
 . 
 DeleteProductInputRequest 
 ( 
 name 
 = 
 _NAME 
 , 
 data_source 
 = 
 _DATA_SOURCE_NAME 
 ) 
 # Makes the request and catch and print any error messages. 
 try 
 : 
 client 
 . 
 delete_product_input 
 ( 
 request 
 = 
 request 
 ) 
 print 
 ( 
 "Deletion successful" 
 ) 
 except 
 RuntimeError 
 as 
 e 
 : 
 print 
 ( 
 "Deletion failed" 
 ) 
 print 
 ( 
 e 
 ) 
 if 
 __name__ 
 == 
 "__main__" 
 : 
 delete_product_input 
 () 
  
 

cURL

  curl -X DELETE \ 
 "https://merchantapi.googleapis.com/products/v1/accounts/ {ACCOUNT_ID} 
/productInputs/en~US~SKU12345?dataSource=accounts/ {ACCOUNT_ID} 
/dataSources/ {DATASOURCE_ID} 
" \ 
 -H "Authorization: Bearer <API_TOKEN>" 
 
Design a Mobile Site
View Site in Mobile | Classic
Share by: