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](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources#DataSource) 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).
- The `ProductInput.name` segment is a unique identifier for the product. Products are identified by a combination of language, feed label and offer ID, in the format `contentLanguage`\~`feedLabel`\~`offerId` such as `en~US~sku12345`. Merchant API uses this identifier to build `product` and `productInput` resource names, for example `accounts/12345/products/en~US~SKU12345`.
- You must [encode](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs#ProductInput.FIELDS.name) the identifier part `en~US~SKU12345` if any part of the product identifier such as the offerId contains URL-reserved characters or characters used by Merchant API such as `/`, `%`, or `~`. For more information, see the [`ProductInput.name`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs#ProductInput.FIELDS.name) field. The identifier part `en~US~SKU12345` must be URL-encoded when included in a request URL.

## 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`](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources/create)
method from the Data Sources sub-API. To help you decide on the type of
data source to create, see
[Choose your data source strategy](https://developers.google.com/merchant/api/guides/compatibility/data-sources#choose-data-strategy)
and
[Manage API data sources for product uploads](https://developers.google.com/merchant/api/guides/data-sources/api-sources).
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.

> [!IMPORTANT]
> **Important:** We recommend "backfilling" your local database with the data source name. Storing the data source name alongside your product data lets you provide it directly in API calls without the overhead of calling [`dataSources.list`](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources/list) before each operation.

Newly created data sources are enabled by default for all
[destinations](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources#destination)
(also called marketing methods) that are available for the account. For more
information about possible destinations, see
[What are Marketing methods?](https://support.google.com/merchants/answer/15130232)

For more information about data sources, see the
[Manage API data sources for product uploads](https://developers.google.com/merchant/api/guides/data-sources/api-sources)
guide.

## 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](https://support.google.com/merchants/answer/7052112).

Use the
[`productInputs.insert`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs/insert)
method and provide the `name` of your primary data source as the `dataSource`
parameter.

The following example inserts a new product for an online store into a primary
data source. The product has some attributes such as:

- a [`title`](https://support.google.com/merchants/answer/6324415)
- a [`description`](https://support.google.com/merchants/answer/6324468)
- a link to an image in the [`imageLink`](https://support.google.com/merchants/answer/6324350) field
- links to videos of the product in the [`videoLinks`](https://support.google.com/merchants/answer/15216925) field
- [`availability`](https://support.google.com/merchants/answer/6324448) information
- [`price`](https://support.google.com/merchants/answer/6324371) information
- product [`condition`](https://support.google.com/merchants/answer/6324469) information
- Global Trade Item Numbers ([GTIN](https://support.google.com/merchants/answer/6324461)) information

For more information about Product attributes, see
[ProductAttributes](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes).

    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",
        "videoLinks": [
          "https://www.example.com/videos/SKU12345_1.mp4",
          "https://www.example.com/videos/SKU12345_2.mp4"
        ],
        "availability": "IN_STOCK",
        "price": {
          "amountMicros": "15990000", // 15.99 USD
          "currencyCode": "USD"
        },
        "condition": "NEW",
        "gtins": ["9780007350896", "93433295494587"],
        "size": "XL",
        "sizeSystem": "US",
        "sizeTypes": [
          "BIG",
          "TALL"
        ]
      }
    }

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.

> [!NOTE]
> **Note:** A successful product input insertion does not mean the corresponding product is approved to show on Google; the approval status can be read through the [`product.productStatus`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products#ProductStatus) field.

The fields returned in a
[`productInputs.insert`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs/insert)
response reflect the input values. The processed values that will be persisted
in the resulting `productAttributes` might differ.

    {
     "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",
        "videoLinks": [
          "https://www.example.com/videos/SKU12345_1.mp4",
          "https://www.example.com/videos/SKU12345_2.mp4"
        ],
       "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 ProductInput 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());

        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("GB")
                          .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());
          return response;
        }
      }

      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


    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(account_id: str, data_source: str):
      """Inserts the specified `ProductInput` resource.

      Args:
        account_id: The Merchant Center account ID.
        data_source: The resource name of the data source that will own the product
          input.

      Returns:
        The inserted `ProductInput` resource.
      """

      # Gets OAuth Credentials.
      credentials = generate_user_credentials.main()

      # Creates a client.
      client = merchant_products_v1.ProductInputsServiceClient(
          credentials=credentials
      )

      parent = f"accounts/{account_id}"

      # 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,
      )

      # 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}")
        return 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.
        raise


    if __name__ == "__main__":
      # Retrieves the Merchant Center account ID from the configuration file.
      merchant_account_id = configuration.Configuration().read_merchant_info()
      # Identifies the data source that will own the product input.
      # You can only insert products into datasource types of Input "API" and
      # "FILE", and of Type "Primary" or "Supplemental."
      target_data_source = (
          f"accounts/{merchant_account_id}/dataSources/[INSERT_DATA_SOURCE_HERE]"
      )

      insert_product_input(merchant_account_id, target_data_source)

### 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',
          'gtins': '[9780007350896]',
          'price': {'amountMicros': '2500000', 'currencyCode': 'USD'},
        },
      };

      try {
        console.log('Sending insert ProductInput request');
        // Call the ProductInputs.insert API method.
        const 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",
        "videoLinks": [
          "https://www.example.com/videos/SKU12345_1.mp4",
          "https://www.example.com/videos/SKU12345_2.mp4"
        ],
       "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
[`productInput.insert`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.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`](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources#DefaultRule).

## 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](https://developers.google.com/merchant/api/guides/send-multiple-requests#write-batch-request).

> [!TIP]
> **Tip:** Improve throughput with channel pools: Configure a channel pool to maximize throughput for bulk operations. This bypasses the typical 100-stream limit of a single gRPC channel by managing multiple underlying connections. For more information, see [Refactor code for concurrent requests: Improve throughput with channel pools](https://developers.google.com/merchant/api/guides/compatibility/refactor-concurrent-requests#channel-pools)

### 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.api.gax.grpc.ChannelPoolSettings;
    import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
    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 a channel provider. This provider manages a pool of gRPC channels
        // to enhance throughput for bulk operations. Each individual channel in the pool
        // can handle up to approximately 100 concurrent requests.
        //
        // Channel: A single connection pathway to the service.
        // Pool: A collection of multiple channels managed by this provider.
        //   Requests are distributed across the channels in the pool.
        //
        // We recommend estimating the number of concurrent requests you'll make, divide by 50 (50%
        // utilization of channel capacity), and set the pool size to that number.
        InstantiatingGrpcChannelProvider channelProvider =
            InstantiatingGrpcChannelProvider.newBuilder()
                .setChannelPoolSettings(ChannelPoolSettings.staticallySized(30))
                .build();

        // Creates service settings using the credentials retrieved above.
        ProductInputsServiceSettings productInputsServiceSettings =
            ProductInputsServiceSettings.newBuilder()
                .setCredentialsProvider(FixedCredentialsProvider.create(credential))
                .setTransportChannelProvider(channelProvider)
                .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 os
    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

    # Enable gRPC connection scaling.
    os.environ["GRPC_EXPERIMENTAL_MAX_CONCURRENT_STREAMS_CONNECTION_SCALING"] = (
        "true"
    )
    os.environ["GRPC_EXPERIMENTS"] = "subchannel_connection_scaling"

    # 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="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="CH",
          offer_id=_generate_random_string(),
          product_attributes=attributes,
      )


    class ClientPool:
      """A simple client pool to distribute requests across multiple clients.

      This implements the Client Pool pattern to enhance throughput for bulk
      operations, mimicking channel pooling.
      """

      def __init__(self, size: int, credentials):
        self._pool = [
            ProductInputsServiceAsyncClient(credentials=credentials)
            for _ in range(size)
        ]
        self._size = size
        self._index = 0

      def get_client(self) -> ProductInputsServiceAsyncClient:
        """Returns the next client in the pool using round-robin."""
        client = self._pool[self._index]
        self._index = (self._index + 1) % self._size
        return client


    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_product_input request.
      """
      return await client.insert_product_input(request=request)


    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 client pool with 30 clients to handle concurrent requests.
      # We recommend estimating the number of concurrent requests you'll make,
      # divide by 50 (50% utilization of channel capacity), and set the pool size to
      # that number.
      client_pool = ClientPool(size=30, credentials=credentials)

      tasks = []
      for _ in range(5):
        product_input = _create_random_product()
        request = InsertProductInputRequest(
            parent=_PARENT,
            data_source=data_source_name,
            product_input=product_input,
        )

        # Get a client from the pool and create the async task
        client = client_pool.get_client()
        insert_product_task = asyncio.create_task(
            async_insert_product_input(client, request)
        )
        tasks.append(insert_product_task)

      print("Sending insert product input requests")

      try:
        # Await all tasks to complete concurrently and gather their results
        results = await asyncio.gather(*tasks)
        print("Inserted products below")
        print(results)
      except RuntimeError as e:
        # Catch and print any exceptions that occur during the API calls.
        print(e)


    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](https://support.google.com/merchants/answer/15130232), 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"]
      }
    }

## Products targeting specific destinations

You can use a single product to target both online and local destination types.
This feature lets you take advantage of the
[multi-channel](https://support.google.com/merchants/answer/16529073)
strategy in shopping.

You control a product's targeting through:

- the configuration if its data source
- its individual product level configuration

> [!IMPORTANT]
> **Key Point:** Products inherit the targeting from their data sources. For more information about data source configuration, see [Create a primary data source for products](https://developers.google.com/merchant/api/guides/data-sources/api-sources#create-primary-data-source).

### All destinations

The default product targeting supports all destinations, similar to the
default targeting of data sources, depending on your
[program participation](https://developers.google.com/merchant/api/guides/programs/overview).

### Online or local only

If you want a product to be available exclusively through online channels, or
only in brick-and-mortar stores:

- **Data source level:** Set the [destinations](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources#Destination) (also known as Marketing methods) of your data source to online or local ones only. For example **local inventory ads** , and **free local listings** are local destinations. For more information about how and where your products are shown on Google to potential customers, see [What are Marketing methods?](https://support.google.com/merchants/answer/15130232) and [Local inventory ads and free local listings overview](https://support.google.com/merchants/answer/14615117).
- **Product level:** Regardless of the data source configuration, you can target specific destinations at the product level. Set the [`includedDestinations`](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes#FIELDS.included_destinations) to online or local destinations only. Alternatively, you can use [`excludedDestinations`](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes#FIELDS.excluded_destinations) to specify what destinations not to target. For example, you can use `excludedDestinations` to prevent an online product from appearing in local destinations.

> [!NOTE]
> **Note:** We recommend setting destinations on data sources level for most use cases. Use the `includedDestinations` or `excludedDestinations` fields within products only to specify exceptions.

- **In-store information for local products:** If you are targeting local destination types for some of your products, you must specify in-store information for them. For more information about adding in-store information to your local products, see [Insert in-store information](https://developers.google.com/merchant/api/guides/inventories/add-local-inventory#insert_in-store_information).

> [!NOTE]
> **Note:** If you are targeting online destination types for some of your products, you can add regional information for them. For more information, see [Insert regional information](https://developers.google.com/merchant/api/guides/inventories/add-regional-inventory#insert_regional_information).

## Get product information

To retrieve the final processed state of a product, use the
[`products.get`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.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/{PRODUCT_ID}

A successful request returns the `Product` resource.

    {
     "name": "accounts/{ACCOUNT_ID}/products/{PRODUCT_ID}}",
     "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.common.io.BaseEncoding;
    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 java.nio.charset.StandardCharsets;
    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 {

      // Base64Url encoder/decoder without padding
      private static final BaseEncoding BASE64URL_NOPADDING = BaseEncoding.base64Url().omitPadding();

      // Encodes a string to base64url without padding
      public static String encodeProductId(String productId) {
        return BASE64URL_NOPADDING.encode(productId.getBytes(StandardCharsets.UTF_8));
      }

      public static Product getProduct(Config config, String accountId, String productId)
          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();

        try (ProductsServiceClient productsServiceClient =
            ProductsServiceClient.create(productsServiceSettings)) {

          // The name has the format: accounts/{account}/products/{productId}
          String name = "accounts/" + accountId + "/products/" + productId;

          // The name has the format: accounts/{account}/products/{productId}
          GetProductRequest request = GetProductRequest.newBuilder().setName(name).build();

          System.out.println("Sending get product request:");
          Product response = productsServiceClient.getProduct(request);

          System.out.println("Retrieved Product below");
          System.out.println(response);
          return response;
        }
      }

      public static void main(String[] args) throws Exception {
        Config config = Config.load();
        String accountId = config.getAccountId().toString();

        // 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 productId = "en~GB~sku123"; // Replace with your actual product ID

        // Uncomment the following line if the product name contains special characters (such as forward
        // slashes) and needs base64url encoding.
        // productId = encodeProductId(productId);

        getProduct(config, accountId, productId);
      }
    }

### PHP

    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\Client\ProductsServiceClient;
    use Google\Shopping\Merchant\Products\V1\GetProductRequest;

    /**
     * This class demonstrates how to get a single product for a given Merchant
     * Center account.
     */
    class GetProductSample
    {
        /**
         * A helper function to create the product name string.
         *
         * @param string $accountId
         *      The account that owns the product.
         * @param string $productId
         *      The ID of the product.
         *
         * @return string The name has the format: `accounts/{account}/products/{product}`
         */
        private static function getName(string $accountId, string $productId): string
        {
            return sprintf("accounts/%s/products/%s", $accountId, $productId);
        }

        /**
         * Encodes a string to base64url without padding. This is needed if the
         * product ID contains special characters (such as forward slashes) and
         * needs base64url encoding.
         *
         * @param string $productId
         *      The ID of the product.
         *
         * @return string The encoded product ID.
         */
        public static function encodeProductId(string $productId): string
        {
            return rtrim(strtr(base64_encode($productId), '+/', '-_'), '=');
        }

        /**
         * Retrieves a product from your Merchant Center account.
         *
         * @param array $config
         *      The configuration data used for authentication and getting the
         *      account ID.
         * @param string $productId
         *      The ID of the product, in the form of
         *      `contentLanguage~feedLabel~offerId`.
         *
         * @return void
         */
        public static function getProduct(array $config, string $productId): 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);

            // The name has the format: accounts/{account}/products/{productId}
            $name = self::getName($config['accountId'], $productId);

            // Creates the request.
            $request = new GetProductRequest([
                'name' => $name
            ]);

            // Calls the API and catches and prints any network failures/errors.
            try {
                print "Sending get product request:\n";
                $response = $productsServiceClient->getProduct($request);
                print "Retrieved Product below\n";
                // Pretty-prints the JSON representation of the response.
                print $response->serializeToJsonString(true) . "\n";
            } catch (ApiException $e) {
                printf("Call failed with message: %s\n", $e->getMessage());
            }
        }

        /**
         * Helper to execute the sample.
         *
         * @return void
         */
        public function callSample(): void
        {
            $config = Config::generateConfig();

            // 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.
            $productId = 'en~US~sku123'; // Replace with your actual product ID

            // Uncomment the following line if the product name contains special
            // characters (such as forward slashes) and needs base64url encoding.
            // $productId = self::encodeProductId($productId);

            self::getProduct($config, $productId);
        }
    }

    // Runs the sample.
    $sample = new GetProductSample();
    $sample->callSample();

### Python

    import base64

    from examples.authentication import configuration
    from examples.authentication import generate_user_credentials
    from google.shopping.merchant_products_v1 import GetProductRequest
    from google.shopping.merchant_products_v1 import Product
    from google.shopping.merchant_products_v1 import ProductsServiceClient


    # This is needed for base64url encoding if product IDs contain special
    # characters such as forward slashes.
    def encode_product_id(product_id_to_encode: str) -> str:
      """Base64url encodes a string without padding.

      Args:
        product_id_to_encode: The product ID string to encode.

      Returns:
        The encoded product ID string.
      """
      encoded_bytes = base64.urlsafe_b64encode(product_id_to_encode.encode("utf-8"))
      return encoded_bytes.rstrip(b"=").decode("utf-8")


    def get_product(account_id_arg: str, product_id_arg: str) -> Product:
      """Retrieves a single product from a Merchant Center account.

      Args:
        account_id_arg: The ID of the Merchant Center account.
        product_id_arg: The ID of the product to retrieve.

      Returns:
        The retrieved `Product` resource.
      """
      # Gets OAuth Credentials.
      credentials = generate_user_credentials.main()

      # Creates a client.
      client = ProductsServiceClient(credentials=credentials)

      # The name has the format: accounts/{account}/products/{product}
      name = f"accounts/{account_id_arg}/products/{product_id_arg}"

      # Creates the request.
      request = GetProductRequest(name=name)

      print("Sending get product request:")

      # Makes the request and catches and prints any error messages.
      try:
        response = client.get_product(request=request)
        print("Retrieved Product below")
        print(response)
        return response
      except RuntimeError as e:
        print(e)
        raise


    if __name__ == "__main__":
      # Retrieves the configured account ID from the config file.
      account_id = configuration.Configuration().read_merchant_info()

      # The ID of the product, which is the final component of the product's
      # resource name. The product ID is the same as the offer ID.
      # For example, `en~US~sku123`.
      product_id = "en~US~sku123"  # Replace with your actual product ID.

      # Uncomment the following line if the product ID contains special characters
      # (such as forward slashes) and needs base64url encoding.
      # product_id = encode_product_id(product_id)

      get_product(account_id, product_id)


### 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:
      // 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.
        const 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/{PRODUCT_ID}" \
    -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`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.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/{PRODUCT_ID}?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.");
        }
      }

      public static void main(String[] args) throws Exception {
        Config config = Config.load();
        // An ID assigned to a product input by Google. In the format
        // contentLanguage~feedLabel~offerId
        String productId = "en~GB~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


    def delete_product_input(account_id: str, product_id: str, data_source: str):
      """Deletes the specified `ProductInput` resource.

      Args:
        account_id: The Merchant Center account ID.
        product_id: The ID of the product input to delete, in the format
          `contentLanguage~feedLabel~offerId`.
        data_source: The resource name of the data source from which to delete the
          product.
      """

      # Gets OAuth Credentials.
      credentials = generate_user_credentials.main()

      # Creates a client.
      client = merchant_products_v1.ProductInputsServiceClient(
          credentials=credentials
      )

      name = f"accounts/{account_id}/productInputs/{product_id}"

      # Creates the request.
      request = merchant_products_v1.DeleteProductInputRequest(
          name=name, data_source=data_source
      )

      # 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)
        raise


    if __name__ == "__main__":
      # Retrieves the Merchant Center account ID from the configuration file.
      merchant_account_id = 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`
      target_product = "[INSERT_PRODUCT_HERE]"
      target_data_source = (
          f"accounts/{merchant_account_id}/dataSources/[INSERT_DATA_SOURCE_HERE]"
      )

      delete_product_input(
          merchant_account_id, target_product, target_data_source
      )

### cURL

    curl -X DELETE \
    "https://merchantapi.googleapis.com/products/v1/accounts/{ACCOUNT_ID}/productInputs/{PRODUCT_ID}?dataSource=accounts/{ACCOUNT_ID}/dataSources/{DATASOURCE_ID}" \
    -H "Authorization: Bearer <API_TOKEN>"