Merchant API introduces a more robust and intuitive way to manage your product
data. The main change is the separation of product data into two distinct
resources: `ProductInput` for submitting your data and `Product` for viewing the
final, processed version including product status and issues. This new structure
provides a more predictable and transparent experience.

This guide walks you through the key differences to help you migrate your
integration from the Content API for Shopping. For a detailed guide on using the
new features, see
[Manage your products](https://developers.google.com/merchant/api/guides/products/overview).

## Key differences

Here are the most significant changes to how you manage products in
Merchant API compared to Content API for Shopping:

- **Dedicated resources for input and processed data** : Merchant API splits
  product management into two resources. You can use the
  [`ProductInput`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs)
  resource to insert, update, and delete your product data. You can use the
  read-only
  [`Product`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products)
  resource to view the final product after Google processes your inputs,
  applies rules, and combines data from supplemental sources.

- **Encoding for product names** : You can use
  **unpadded base64url (RFC 4648 Section 5) encoding** for both
  [`ProductInput.name`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs)
  and
  [`Product.name`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products)
  fields. In case the product names contain characters used by Merchant API
  or URL-reserved characters, encoding is **mandatory**. For example, you must
  encode the product names if they contain any of the following characters:

      % . + / : \~ , ( \* ! ) \& ? = @ # $

- **Integrated product status** : The `productstatuses` service is removed.
  Product validation issues and destination statuses are now directly included
  in the
  [`Product`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products)
  resource within the `productStatus` field, simplifying data retrieval.

- **Predictable product updates** : The new
  [`productInputs.patch`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs/patch)
  method modifies a specific product input directly. This is a significant
  improvement over the Content API for Shopping, where updates could be
  unexpectedly overwritten by other feed uploads. In Merchant API, an update
  remains until that specific product input is updated again or deleted.
  Product updates are applied on
  [`ProductInput`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs)
  resource instead of processed
  [`Product`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products)
  resource.

- **Choose your data source for cleaner data management** : All `productInputs`
  write operations now require a `dataSource` query parameter, making it
  explicit which data source you are modifying. This is especially useful if
  you have multiple sources providing data.

- **New resource identifiers** : Products are now identified by a RESTful
  resource `name` instead of the `id` field. The format is
  `accounts/{account}/products/{product}`.

- **No custom batches** : The `custombatch` method is no longer available. You
  can use [asynchronous
  requests](https://developers.google.com/merchant/api/samples/insert-product-input-async) or [HTTP
  batching](https://developers.google.com/merchant/api/guides/send-multiple-requests) to send multiple
  requests in a single HTTP call.

> [!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)

### Data source guidelines during migration

> [!IMPORTANT]
> **Key Term:** "Offer stealing" occurs when a product identified by its unique key (`language`, `feedlabel`, `offerID`) is inserted into a different primary data source than the one it belongs to. This effectively deletes the product from the old source and moves it to the new one, where different data source rules and ownership settings may apply. While stealing can be used intentionally to reorganize products, accidental stealing can occur if an incorrect `dataSource` is supplied during insertion.

Before you migrate your data sources, we strongly recommend you to
[choose your data source strategy](https://developers.google.com/merchant/api/guides/compatibility/data-sources#choose-data-strategy).

To ensure a smooth migration and prevent issues such as offer stealing, follow
these recommendations:

- **Backfill your database** : Instead of calling
  [`dataSources.list`](https://developers.google.com/merchant/api/reference/rest/datasources_v1/accounts.dataSources/list)
  before every product operation, we strongly recommend you to perform a
  one-time backfill of your local database. Add a `dataSource` name field to
  each product record so you can provide the correct identifier directly in
  your requests.

- **Consolidate and use data sources for any label and language**:
  Merchant API makes it possible to create a data source without specifying
  label and language and therefore allows inserting products with any
  data source label and language. Consider using a single data source for any
  label and language.

- **Protect your products** : If you use data source rules, call
  [`products.get`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products/get)
  to find the exact `dataSource` associated with a product before updating or
  deleting it. This ensures you are modifying the intended source and
  prevents accidental offer stealing.

> [!NOTE]
> **Note:** Make sure to use the same `offerId` for the same product when calling Merchant API as you did with Content API for Shopping to avoid losing your product history. Check also the [Identifiers](https://developers.google.com/merchant/api/guides/compatibility/products#identifiers) section in this page.

## Requests

This section compares the request formats for Content API for Shopping and
Merchant API.

| Request description | Content API for Shopping | Merchant API |
|---|---|---|
| Get a product | `GET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}` | `GET https://merchantapi.googleapis.com/products/v1/accounts/{account}/products/{product}` |
| List products | `GET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products` | `GET https://merchantapi.googleapis.com/products/v1/accounts/{account}/products` |
| Insert a product | `POST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products` | `POST https://merchantapi.googleapis.com/products/v1/accounts/{account}/productInputs:insert` |
| Update a product | `PATCH https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}` | `PATCH https://merchantapi.googleapis.com/products/v1/accounts/{account}/productInputs/{productinput}` |
| Delete a product | `DELETE https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}` | `DELETE https://merchantapi.googleapis.com/products/v1/accounts/{account}/productInputs/{productinput}` |
| Get product status | `GET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/productstatuses/{productId}` | `GET https://merchantapi.googleapis.com/products/v1/accounts/{account}/products/{product}` |
| List product statuses | `GET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/productstatuses` | `GET https://merchantapi.googleapis.com/products/v1/accounts/{account}/products` |
| Batch multiple requests | `POST https://shoppingcontent.googleapis.com/content/v2.1/products/custombatch` | Use asynchronous requests or HTTP batching |

## Identifiers

The format for product identifiers has changed in Merchant API to a standard
REST resource name.

| Identifier description | Content API for Shopping | Merchant API |
|---|---|---|
| Product ID | A string composed of segments separated by a colon (`:`). Format: `channel:contentLanguage:targetCountry:offerId` or `channel:contentLanguage:feedLabel:offerId`. Example: `online:en:US:sku123` | A REST resource `name` string. Format: `accounts/{account}/products/{product}` where `{product}` is `contentLanguage~feedLabel~offerId`. Example: `accounts/12345/products/en~US~sku123`. Encoding: **Unpadded base64url encoding** recommended and **mandatory** in case of product ids that contain characters used by Merchant API or URL-reserved characters. |

> [!NOTE]
> **Note:** The `channel` field is no longer present in Merchant API. Products with `LOCAL` channel in Content API for Shopping should instead set `legacy_local` field to true.

## Methods

This table shows the Content API for Shopping methods and their equivalents in
Merchant API.

| Content API for Shopping method | Merchant API method | Availability \& notes |
|---|---|---|
| `products.get` | [`products.get`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products/get) | Retrieves the final, processed product. |
| `products.list` | [`products.list`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products/list) | Lists final, processed products. |
| `products.insert` | [`productInputs.insert`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs/insert) | Inserts a product input. Requires a `dataSource`. |
| `products.update` | [`productInputs.patch`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs/patch) | The behavior is significantly different. It updates a specific product input and is persistent. |
| `products.delete` | [`productInputs.delete`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs/delete) | Deletes a specific product input. Requires a `dataSource`. |
| `products.custombatch` | Not available | Use asynchronous requests or HTTP batching. |
| `productstatuses.get` | [`products.get`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products/get) | The `productstatuses` service is removed. Status information is now part of the `Product` resource. |
| `productstatuses.list` | [`products.list`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.products/list) | The `productstatuses` service is removed. Status information is now part of the `Product` resource. |
| `productstatuses.custombatch` | Not available | Use [asynchronous requests](https://developers.google.com/merchant/api/samples/insert-product-input-async) or [HTTP batching](https://developers.google.com/merchant/api/guides/send-multiple-requests). |

## Detailed field changes

This table highlights important fields that have been changed, added, or removed
in the Merchant API.

| Content API for Shopping | Merchant API | Description |
|---|---|---|
| `id` | `name` | The primary identifier for a product is now the REST resource `name`. **Unpadded base64url encoding** recommended and **mandatory** in case of product names that contain characters used by Merchant API or URL-reserved characters. |
| Top-level product data specification attributes (e.g., `title`, `price`, `link`) | `productAttributes` object | Product attributes like `title`, `price`, and `link` are no longer top-level fields. They are now grouped within the `productAttributes` object in both the `Product` and `ProductInput` resources. This provides a cleaner and more organized resource structure. |
| `targetCountry` | `feedLabel` | The resource name now uses `feedLabel` instead of `targetCountry` to align with Merchant Center functionality. |
| `feedId` | `dataSource` (query parameter) | A `dataSource` name is now a required query parameter for all `productInputs` write methods (`insert`, `update`, `delete`). |
| `channel` | Not available. Use `legacy_local` for local only products. | The `channel` field is no longer present in Merchant API. Products with `LOCAL` channel in Content API for Shopping should instead set `legacy_local` field to true. |
| Not available | `versionNumber` | A new optional field on [`ProductInput`](https://developers.google.com/merchant/api/reference/rest/products_v1/accounts.productInputs) that can be used to prevent out-of-order insertions into primary data sources. |
| `string` type fields with defined set of values | `enum` type fields with defined set of values | Fields within product attributes with defined set of values (for example `excluded_destinations`, `availability`) are now `enum` type. |
| `gtin` | [`productAttributes.gtins`](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes#FIELDS.gtins) | Changed from a singular string (`gtin`) to a repeated string (`gtins`). |
| `sizes` | [`productAttributes.size`](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes#FIELDS.size) | Changed from a repeated string (`sizes`) to a singular string (`size`). |
| `sizeType`, `additionalSizeType` | [`productAttributes.sizeTypes`](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes#FIELDS.size_types) | Replaced `sizeType` and `additionalSizeType` with a repeated [`SizeType`](https://developers.google.com/merchant/api/reference/rest/products_v1/ProductAttributes#SizeType) enum (`sizeTypes`) that accepts up to two values (for example, `["PETITE", "MATERNITY"]`). |