Content API for Shopping compatibility

Use this guide to integrate Merchant API with your existing Content API for Shopping implementation. It describes general changes that apply to the entire Merchant API. Specific pages (see the navigation) describe how to migrate specific features in more detail.

Work alongside Content API for Shopping

Google has designed Merchant API to work alongside existing Content API for Shopping v2.1 features.

For example, you can use the Merchant Inventories API alongside your existing Content API for Shopping v2.1 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.

Improvements over Content API

Merchant API improves over Content API in the following areas:

Consider these changes in more detail.

Versioning and sub-APIs

Merchant API introduces the concepts of versioning and sub-APIs. Its modular design improves ease of use, by allowing you to focus on the sub-APIs you need and enabling easier future migrations to newer versions. Versioning will be applied with your request URLs. The strategy is similar to the Google Ads API experience.

More robust requests

Merchant API URL requests require more parameters to call Merchant API. This includes the resource, version, name (identifiers) and method (non standard methods). For more about this, see Account and Product identifiers and examples.

AIP principles for identifiers

While Content API for Shopping uses IDs to identify resources (for example, merchantId, productId), Merchant API uses a name identifier to align with the AIP (see API improvement principles).

The {name}identifier includes the resource identifier and its parent (or potentially multiple parents), such that {name} equals accounts/{account}/products/{product}

All read and write calls return the name field as the resource identifier.

{name} also includes the collection identifiers accounts/ and products/.

Merchant API uses {account} to refer to a Merchant Center ID and {product} to refer to product identifiers.

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 merchant and resource IDs yourself.

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

   POST https://merchantapi.googleapis.com/inventories/v1beta/{PARENT}/regionalInventories:insert

The table shows how the Content API for Shopping products.get request changes:

Content API for Shopping Merchant API
GET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId} GET https://merchantapi.googleapis.com/products/v1beta/{name}

For more details, review Identifier changes.

As another example, retrieving a product with the identifier online~en~US~1234 from Merchant Center ID 4321 using Merchant API would look like the following:

    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 Merchant API.

In Content API for Shopping, a colon (:) denotes a delimiter in product name whereas, in Merchant API, a tilde (~) performs this function.

For example, product ID in Content API for Shopping:

channel:contentLanguage:feedLabel:offerId.

in Merchant Center API becomes the following:

channel~contentLanguage~feedLabel~offerId.

Parent fields for child resources

In 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 into, instead of passing the entire parent resource. You can also use the parent field with list

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

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

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

Common enums

The use of common enums provides more consistency.

The Destination.DestinationEnum field specifies the surfaces on which to display your resources. DestinationEnum lists all available values for destination targeting and is unified across sub-APIs, for example for promotions attributes.

The ReportingContext.ReportingContextEnum field represents the context to which your account and product issues apply. This field is used across reporting methods (for example, for IssueSeverityPerReportingContext).

Backwards compatibility

As you start using Merchant API, your existing Content API for Shopping integration continues to function without interruption. For more information, see Compatibility.

Once you migrate your sub-APIs to Merchant API, we recommend you use only Merchant API for your migrated sub-APIs.

Remote procedure call (gRPC) availability

gRPC is the new recommended way to integrate with Merchant API.

Its advantages include the following:

Custom batching becomes built-in batching

Batching performs more efficiently when you use asynchronous calls. Learn more about using parallel calls to achieve batching in Merchant API and how to Refactor code for concurrent requests.

To help expedite your migration, we recommend the client libraries.

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 Java sample demonstrates how to insert a product input.

   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, and need this feature for the Merchant API, let us know why in your feedback.

Exclusive features

Future features will appear only in Merchant API. (There will be a few exceptions, such as the 2025 annual feed spec.)

Features exclusive to Merchant API include

  • Reviews API. Use Reviews to implement and manage your product and shop ratings. For more information see Seller review and Product Review.
  • Notifications: Sign up to receive push notifications for changes to an account's product data.

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 continues to be ISO 4217.

Latest updates and announcements

For more granular updates, see the release notes specific to each sub-API. For more regular aggregated Merchant API updates, review our latest updates.

For more specific details and to learn even more about Merchant API, view our developer site overview and overall migration guide for more details.

See Merchant API design for details about Merchant API and its sub-APIs.