Migrate from Content API for Shopping to Merchant API

This guide explains the migration process from Content API for Shopping to Merchant API for business data management.

You can use this guide to migrate your existing Content API for Shopping implementation to Merchant API. For more information about the details of Merchant API and its sub-APIs, see Merchant API design.

Get started

To start using Merchant API, change your request URLs to the following format:

https://merchantapi.googleapis.com/{SUB_API}/{VERSION}/{RESOURCE_NAME}:{METHOD}

For more information, see the quickstart guide and the Merchant API reference.

See the latest updates in Merchant API (beta).

Improvements over Content API for Shopping

The Merchant API lets you automate and streamline workflows in Merchant Center and offers enhanced capabilities over Content API for Shopping.

Key use cases:

  • Automated account management
  • Automated product management
  • Automated inventory management
  • Custom reporting

Key improvement areas:

What's changed:

  • The maximum pageSize increased from 250 to 1000 rows per API call.
  • A delay that existed for product insertion, promotions, product reviews, and merchant reviews after DataSources creation is fixed.

What's coming:

  • Deprecation and future removal of the channel field for DataSources and products.
  • Launch of an updated definition for clickPotentialRank in the productView table under the Reporting sub-API: * Ranking of products based on clickPotential is normalized to values between 1 and 1000.
    • Products with low clickPotentialRank still have the highest click potential among the merchant's products that fulfill the search query conditions. This is a non-breaking change that might be launched on July 1, 2025.
  • The AccountIdAlias in the AccountRelationship resource, makes it possible to better manage complex account structures. For example, marketplaces use a user-defined alias instead of the merchant's internal ID, such as account ID.

gRPC support

The Merchant API supports gRPC and REST. You can use gRPC for the Merchant API and REST for the Content API for Shopping at the same time.

The Merchant API client libraries require gRPC.

For more information, see gRPC overview.

Compatibility

This guide describes general changes that apply to the entire Merchant API.

The Merchant API is designed to work alongside existing Content API for Shopping features.

For example, you can use the Merchant Inventories API alongside your existing Content API for Shopping products implementation. You might use the Content API for Shopping to upload a new local product (that you sell in a local store), then use the Merchant Inventories API LocalInventory resource to manage in-store information for that product.

Batch requests

The Merchant API doesn't support the customBatch method featured in the Content API for Shopping. Instead, see Send multiple requests at once or execute your calls asynchronously.

The following sample demonstrates how to insert a product inputs asynchronously.

Java

import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.shopping.merchant.products.v1beta.Attributes;
import com.google.shopping.merchant.products.v1beta.InsertProductInputRequest;
import com.google.shopping.merchant.products.v1beta.ProductInput;
import com.google.shopping.merchant.products.v1beta.ProductInputsServiceClient;
import com.google.shopping.merchant.products.v1beta.ProductInputsServiceSettings;
import com.google.shopping.merchant.products.v1beta.Shipping;
import com.google.shopping.type.Channel.ChannelEnum;
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();

    Attributes attributes =
        Attributes.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("in stock")
            .setCondition("new")
            .setGoogleProductCategory("Media > Books")
            .addGtin("9780007350896")
            .addShipping(shipping)
            .addShipping(shipping2)
            .build();

    return ProductInput.newBuilder()
        .setChannel(ChannelEnum.ONLINE)
        .setContentLanguage("en")
        .setFeedLabel("CH")
        .setOfferId(generateRandomString())
        .setAttributes(attributes)
        .build();
  }

  public static void asyncInsertProductInput(Config config, String dataSource) throws Exception {

    // Obtains OAuth token based on the user's configuration.
    GoogleCredentials credential = new Authenticator().authenticate();

    // Creates service settings using the credentials retrieved above.
    ProductInputsServiceSettings productInputsServiceSettings =
        ProductInputsServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credential))
            .build();

    // Creates parent to identify where to insert the product.
    String parent = getParent(config.getAccountId().toString());

    // Calls the API and catches and prints any network failures/errors.
    try (ProductInputsServiceClient productInputsServiceClient =
        ProductInputsServiceClient.create(productInputsServiceSettings)) {

      // Creates five insert product input requests with random product IDs.
      List<InsertProductInputRequest> requests = new ArrayList<>(5);
      for (int i = 0; i < 5; i++) {
        InsertProductInputRequest request =
            InsertProductInputRequest.newBuilder()
                .setParent(parent)
                // You can only insert products into datasource types of Input "API", and of Type
                // "Primary" or "Supplemental."
                // This field takes the `name` field of the datasource.
                .setDataSource(dataSource)
                // If this product is already owned by another datasource, when re-inserting, the
                // new datasource will take ownership of the product.
                .setProductInput(createRandomProduct())
                .build();

        requests.add(request);
      }

      System.out.println("Sending insert product input requests");
      List<ApiFuture<ProductInput>> futures =
          requests.stream()
              .map(
                  request ->
                      productInputsServiceClient.insertProductInputCallable().futureCall(request))
              .collect(Collectors.toList());

      // Creates callback to handle the responses when all are ready.
      ApiFuture<List<ProductInput>> responses = ApiFutures.allAsList(futures);
      ApiFutures.addCallback(
          responses,
          new ApiFutureCallback<List<ProductInput>>() {
            @Override
            public void onSuccess(List<ProductInput> results) {
              System.out.println("Inserted products below");
              System.out.println(results);
            }

            @Override
            public void onFailure(Throwable throwable) {
              System.out.println(throwable);
            }
          },
          MoreExecutors.directExecutor());

    } catch (Exception e) {
      System.out.println(e);
    }
  }

  public static void main(String[] args) throws Exception {
    Config config = Config.load();
    // Identifies the data source that will own the product input.
    String dataSource = "accounts/" + config.getAccountId() + "/dataSources/{datasourceId}";

    asyncInsertProductInput(config, dataSource);
  }
}

If you use customBatch in Content API for Shopping, and need this feature for the Merchant API, let us know why in your feedback.

Identifiers

To align with Google's API improvement principles, we've made some changes to the identifiers for Merchant API resources.

The use of IDENTIFIER indicates that a field within a resource message is used to identify the resource. It is attached to the name field, see fields representing resource names.

The IDENTIFIER value conveys that the field isn't accepted as input (that is, OUTPUT_ONLY) in the context of a create method, while also being considered IMMUTABLE and accepted as input for mutation methods that accept the resource as the primary input example: Standard Update.

name replaces Id

All Merchant API resources use the name field as their unique identifier.

Here's an example of how to use the name field in your calls:

GET https://merchantapi.googleapis.com/products/v1beta/accounts/4321/products/online~en~US~1234

where name equals accounts/4321/products/online~en~US~1234.

This new name field is returned as the resource identifier for all read and write calls in the Merchant API.

For example, implement a getName() method to retrieve the name from a resource, and store the output as a variable instead of constructing the name from the business and resource IDs yourself.

Delimiters

Merchant API uses tildes instead of colons as delimiters.

The following table compares the product ID for Content API for Shopping and Merchant API:

Content API for Shopping Merchant API
channel:contentLanguage:feedLabel:offerId. For example, online:en:US:sku123 channel~contentLanguage~feedLabel~offerId. For example, online~en~US~sku123

parent fields for child resources

In the Merchant API, all child resources have the parent field. You can use the parent field to specify the name of the resource to insert the child to, instead of passing the entire parent resource. You can also use the parent field with list methods to list the child resources of that parent.

For example, to list local inventories for a given product, specify the product's name in the parent field for the list method. In this case, the given product is the parent of the LocalInventory resources returned.

GET https://merchantapi.googleapis.com/inventories/v1beta/{parent}/localInventories

To retrieve all local inventories for product online~en~US~1234 and account 4321, the request would look like this:

GET https://merchantapi.googleapis.com/inventories/v1beta/accounts/4321/products/online~en~US~1234/localInventories

where parent equals accounts/{account}/products/{product}. Note that in this case the localInventories have two parents included in the name identifier (accounts/ and products/) as the account is the parent of the product resource.

Common Types

Here are some common types shared across the sub-APIs of Merchant API.

Destination.DestinationEnum

Use Destination.DestinationEnum field to control on which surfaces your resources should be displayed. The DestinationEnum field lists all available values for destination targeting. The APIs unify DestinationEnum across multiple sub-APIs, for example for promotions attributes.

ReportingContext.ReportingContextEnum

ReportingContext.ReportingContextEnum field represents context for which your account and product issues apply. This field is used across reporting methods (such as for IssueSeverityPerReportingContext).

Price

Here's what's changed for Price in the Merchant Common package:

Content API for Shopping Merchant API
Amount field value:string amountMicros:int64
Currency field currency:string currencyCode:string

The Price amount is now recorded in micros, where 1 million micros is equivalent to your currency's standard unit.

In the Content API for Shopping, Price was a decimal number in the form of a string.

The amount field name has changed from value to amountMicros

The currency field name has changed from currency to currencyCode. The format remains as ISO 4217.