AI-generated Key Takeaways
-
This content provides PHP and Python code samples for using the Merchant API to insert product inputs into a Merchant Center account.
-
The code demonstrates how to define product attributes like title, description, price, shipping, availability, and GTIN within a
ProductInput
object. -
It illustrates the process of creating a request with necessary parameters such as parent account, data source, and the product input object to make the call to the Merchant Center server.
-
The samples show how to set up the required credentials and create a client for interacting with the Merchant Center Product Inputs service, making API calls to insert products.
-
It highlights the importance of providing a unique combination of channel, feed label, content language, and offer ID for each product input, as the API will replace existing entries if these match.
Merchant API code sample to insert product input.
AppsScript
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* 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);
}
}
CSharp
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Google.Apis.Auth.OAuth2;
using Google.Shopping.Merchant.Products.V1;
using Google.Shopping.Type;
using System;
using static MerchantApi.Authenticator;
using Price = Google.Shopping.Type.Price;
namespace MerchantApi
{
public class InsertProductInputSample
{
/// Inserts a product input into a specified data source.
public void InsertProductInput(string merchantId, string dataSource)
{
Console.WriteLine("=================================================================");
Console.WriteLine("Inserting a ProductInput into a data source...");
Console.WriteLine("=================================================================");
// Authenticate using either oAuth or service account
ICredential auth = Authenticator.Authenticate(
MerchantConfig.Load(),
ProductsServiceClient.DefaultScopes[0]);
// Creates the ProductInputsServiceClient.
var productInputsServiceClient = new ProductInputsServiceClientBuilder
{
Credential = auth
}.Build();
// The parent account.
string parent = $"accounts/{merchantId}";
// The price for shipping.
Price price = new Price { AmountMicros = 33_450_000, CurrencyCode = "USD" };
// Shipping information for Great Britain.
Shipping shipping = new Shipping
{
Price = price,
Country = "GB",
Service = "1st class post"
};
// Shipping information for France.
Shipping shipping2 = new Shipping
{
Price = price,
Country = "FR",
Service = "1st class post"
};
// The attributes of the product.
ProductAttributes 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",
ImageLink = "https://exampleWebsite.com/tale-of-two-cities.jpg",
Availability = Availability.InStock,
Condition = Condition.New,
GoogleProductCategory = "Media > Books",
Gtins = { "9780007350896" },
Shipping = { shipping, shipping2 }
};
// The product input to insert.
ProductInput productInput = new ProductInput
{
ContentLanguage = "en",
FeedLabel = "US",
OfferId = "sku123",
ProductAttributes = attributes
};
// Creates the request to insert the product input.
// The data source can be a primary or supplemental data source.
// You can only insert products into data sources of type "API" or "FILE".
InsertProductInputRequest request = new InsertProductInputRequest
{
Parent = parent,
DataSource = dataSource,
ProductInput = productInput
};
try
{
Console.WriteLine("Sending insert ProductInput request...");
ProductInput response = productInputsServiceClient.InsertProductInput(request);
Console.WriteLine("Inserted ProductInput Name below:");
// The last part of the product input name is the product ID assigned by Google.
// Format: `contentLanguage~feedLabel~offerId`
Console.WriteLine(response.Name);
Console.WriteLine("Inserted Product Name below:");
Console.WriteLine(response.Product);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void Main(string[] args)
{
MerchantConfig config = MerchantConfig.Load();
string merchantId = config.MerchantId.Value.ToString();
// The ID of the data source that will own the product input.
// This field takes the `name` of the data source.
// Replace {INSERT_DATASOURCE_ID} with your actual data source ID.
string dataSourceId = "{INSERT_DATASOURCE_ID}";
string dataSource = $"accounts/{merchantId}/dataSources/{dataSourceId}";
var sample = new InsertProductInputSample();
sample.InsertProductInput(merchantId, dataSource);
}
}
}
Java
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shopping.merchant.samples.products.v1;
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);
}
}
Node.js
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
'use strict';
const fs = require('fs');
const authUtils = require('../../authentication/authenticate.js');
const {
ProductInputsServiceClient,
} = require('@google-shopping/products').v1;
const {
protos,
} = require('@google-shopping/products');
const Availability = protos.google.shopping.merchant.products.v1.Availability;
const Condition = protos.google.shopping.merchant.products.v1.Condition;
/**
* Inserts a product input for a given Merchant Center account and data source.
* @param {!object} config - The configuration object.
* @param {string} dataSource - The data source name.
*/
async function insertProductInput(config, dataSource) {
// Read merchant_id from merchant-info.json.
const merchantInfo = JSON.parse(
fs.readFileSync(config.merchantInfoFile, 'utf8')
);
const merchantId = merchantInfo.merchantId;
// Construct the parent resource name.
// Format: accounts/{account}
const parent = `accounts/${merchantId}`;
// Get credentials.
const authClient = await authUtils.getOrGenerateUserCredentials();
// Create options object for the client.
const options = {authClient: authClient};
// Create the ProductInputsServiceClient.
const productInputsClient = new ProductInputsServiceClient(options);
// Define the price objects.
const shippingPrice = {
amountMicros: 3000000, // 3 USD
currencyCode: 'USD',
};
const price = {
amountMicros: 33450000, // 33.45 USD
currency_code: 'USD',
};
// Define shipping details.
const shipping1 = {
price: shippingPrice,
country: 'GB',
service: '1st class post',
};
const shipping2 = {
price: shippingPrice,
country: 'FR',
service: '1st class post',
};
// Define product attributes.
const attributes = {
title: 'A Tale of Two Cities',
description: 'A classic novel about the French Revolution',
link: 'https://exampleWebsite.com/tale-of-two-cities.html',
imageLink: 'https://exampleWebsite.com/tale-of-two-cities.jpg',
availability: Availability.IN_STOCK,
condition: Condition.NEW,
googleProductCategory: 'Media > Books',
gtins: ['9780007350896'], // GTIN is a repeated field
shipping: [shipping1, shipping2], // Shipping is a repeated field
price: price,
};
// Define the product input object.
const productInput = {
contentLanguage: 'en',
feedLabel: 'label',
offerId: 'sku123',
productAttributes: attributes,
};
// Construct the request object.
const request = {
parent: 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.
// If this product is already owned by another datasource, when re-inserting,
// the new datasource will take ownership of the product.
dataSource: dataSource,
productInput: productInput,
};
try {
console.log('Sending insert ProductInput request');
// Call the API to insert the product input.
const [response, options, rawResponse] = await productInputsClient.insertProductInput(request);
console.log('Inserted ProductInput Name below');
// The last part of the product name will be the product ID assigned by Google.
// Product ID has the format `contentLanguage~feedLabel~offerId`
console.log(response.name);
console.log('Inserted Product Name below');
console.log(response.product);
console.log('Inserted Product below');
console.log(response);
} catch (error) {
console.error(error.message);
}
}
/**
* Main function to call the insertProductInput function.
*/
async function main() {
const config = authUtils.getConfig();
// Read merchant_id from merchant-info.json.
const merchantInfo = JSON.parse(
fs.readFileSync(config.merchantInfoFile, 'utf8')
);
const merchantId = merchantInfo.merchantId;
// Identifies the data source that will own the product input.
// Replace {INSERT_DATASOURCE_ID} with the actual ID of your data source.
// Format: accounts/{account}/dataSources/{dataSource}
const dataSource = `accounts/${merchantId}/dataSources/{INSERT_DATASOURCE_ID}`;
await insertProductInput(config, dataSource);
}
main();
PHP
<?php
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require_once __DIR__ . '/../../../vendor/autoload.php';
require_once __DIR__ . '/../../Authentication/Authentication.php';
require_once __DIR__ . '/../../Authentication/Config.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
# -*- coding: utf-8 -*-
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A module to insert a Product Input."""
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()