商品を追加、管理する

Products サブ API を使用すると、Merchant Center で商品のライフサイクル全体を管理できます。新しい商品のアップロード、既存の商品の更新、商品情報の取得、販売しなくなった商品の削除に使用できます。

特記事項

  • 商品を追加、更新、削除できるのは、タイプ APIデータソースに属している場合のみです。ファイルベースのアップロードを使用するデータソース内の商品を挿入または更新することはできません。
  • 商品を関連性の高い状態に保ち、有効期限切れを防ぐには、定期的な頻度(少なくとも 30 日ごと)で商品を更新または更新する必要があります。

前提条件

API を使用して商品を追加または管理するには、Merchant Center アカウントで少なくとも 1 つのデータソースが構成されている必要があります。

データソースを作成するには、Data Sources サブ API の dataSources.create メソッドを使用します。詳細な手順については、商品アップロード用の API データソースを管理するガイドをご覧ください。データソースを作成したら、name(例: accounts/12345/dataSources/67890)をメモしておきます。これは、商品関連の作成、更新、削除のリクエストで必要になります。

商品を追加

Merchant Center アカウントに新しい商品を追加するには、ProductInput リソースをメインのデータソースに挿入する必要があります。このアクションは、商品を作成して処理を開始します。productAttributes オブジェクト内のフィールドは、商品データ仕様に準拠している必要があります。

productInputs.insert メソッドを使用して、メインのデータソースの namedataSource パラメータとして指定します。

この例では、オンライン ショップの新製品をプライマリ データソースに挿入します。

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",
    "availability": "IN_STOCK",
    "price": {
      "amountMicros": "15990000", // 15.99 USD
      "currencyCode": "USD"
    },
    "condition": "NEW",
    "gtins": ["9780007350896", "93433295494587"]
  }
}

呼び出しが成功すると、新しく作成された ProductInput リソースが返されます。name フィールドにはこの商品入力の一意の識別子が含まれ、product フィールドには後で取得するために使用できる最終的な処理済み商品の名前が含まれます。

{
 "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",
   "availability": "IN_STOCK",
   "price": {
     "amountMicros": "15990000",
     "currencyCode": "USD"
   },
   "condition": "NEW",
   "gtins": [
     "9780007350896"
   ]
 }
}

次のコードサンプルは、商品を追加する方法を示しています。

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 void insertProductInput(Config config, String dataSource) throws Exception {

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

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

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

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

      // Price to be used for shipping ($33.45).
      Price price = Price.newBuilder().setAmountMicros(33_450_000).setCurrencyCode("USD").build();

      Shipping shipping =
          Shipping.newBuilder()
              .setPrice(price)
              .setCountry("GB")
              .setService("1st class post")
              .build();

      Shipping shipping2 =
          Shipping.newBuilder()
              .setPrice(price)
              .setCountry("FR")
              .setService("1st class post")
              .build();

      ProductAttributes attributes =
          ProductAttributes.newBuilder()
              .setTitle("A Tale of Two Cities")
              .setDescription("A classic novel about the French Revolution")
              .setLink("https://exampleWebsite.com/tale-of-two-cities.html")
              .setImageLink("https://exampleWebsite.com/tale-of-two-cities.jpg")
              .setAvailability(Availability.IN_STOCK)
              .setCondition(Condition.NEW)
              .setGoogleProductCategory("Media > Books")
              .addGtins("9780007350896")
              .addShipping(shipping)
              .addShipping(shipping2)
              .build();

      // The datasource can be either a primary or supplemental datasource.
      InsertProductInputRequest request =
          InsertProductInputRequest.newBuilder()
              .setParent(parent)
              // You can only insert products into datasource types of Input "API" and "FILE", and
              // of Type "Primary" or "Supplemental."
              // This field takes the `name` field of the datasource.
              .setDataSource(dataSource)
              // If this product is already owned by another datasource, when re-inserting, the
              // new datasource will take ownership of the product.
              .setProductInput(
                  ProductInput.newBuilder()
                      .setContentLanguage("en")
                      .setFeedLabel("label")
                      .setOfferId("sku123")
                      .setProductAttributes(attributes)
                      .build())
              .build();

      System.out.println("Sending insert ProductInput request");
      ProductInput response = productInputsServiceClient.insertProductInput(request);
      System.out.println("Inserted ProductInput Name below");
      // The last part of the product name will be the product ID assigned to a product by Google.
      // Product ID has the format `contentLanguage~feedLabel~offerId`
      System.out.println(response.getName());
      System.out.println("Inserted Product Name below");
      System.out.println(response.getProduct());
    } catch (Exception e) {
      System.out.println(e);
    }
  }

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

    insertProductInput(config, dataSource);
  }
}

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

_ACCOUNT = configuration.Configuration().read_merchant_info()
_PARENT = f"accounts/{_ACCOUNT}"

# You can only insert products into datasource types of Input "API" and
# "FILE", and of Type "Primary" or "Supplemental."
_DATA_SOURCE = "[INSERT_DATA_SOURCE_HERE]"
_DATA_SOURCE_NAME = f"accounts/{_ACCOUNT}/dataSources/{_DATA_SOURCE}"


def create_product_input():
  """Creates a `ProductInput` resource."""

  # Creates a shipping setting
  price = Price()
  price.amount_micros = 33_450_000
  price.currency_code = "GBP"

  shipping_option_1 = merchant_products_v1.Shipping()
  shipping_option_1.price = price
  shipping_option_1.country = "GB"
  shipping_option_1.service = "1st class post"

  price2 = Price()
  price2.amount_micros = 33_450_000
  price2.currency_code = "EUR"

  shipping_option_2 = merchant_products_v1.Shipping()
  shipping_option_2.price = price2
  shipping_option_2.country = "FR"
  shipping_option_2.service = "2nd class post"

  # Sets product attributes. Make sure to replace these values with your own.
  attributes = merchant_products_v1.ProductAttributes()
  attributes.title = "A Tale of Two Cities"
  attributes.description = "A classic novel about the French Revolution"
  attributes.link = "https://exampleWebsite.com/tale-of-two-cities.html"
  attributes.image_link = "https://exampleWebsite.com/tale-of-two-cities.jpg"
  attributes.price = price
  attributes.availability = Availability.IN_STOCK
  attributes.condition = Condition.NEW
  attributes.google_product_category = "Media > Books"
  attributes.gtins = ["9780007350896"]
  attributes.shipping = [shipping_option_1, shipping_option_2]

  return merchant_products_v1.ProductInput(
      content_language="en",
      feed_label="GB",
      offer_id="sku123",
      product_attributes=attributes,
  )


def insert_product_input():
  """Inserts the specified `ProductInput` resource."""

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

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

  # Creates the request.
  request = merchant_products_v1.InsertProductInputRequest(
      parent=_PARENT,
      # If this product is already owned by another datasource, when
      # re-inserting, the new datasource will take ownership of the product.
      product_input=create_product_input(),
      data_source=_DATA_SOURCE_NAME,
  )

  # Makes the request and catches and prints any error messages.
  try:
    response = client.insert_product_input(request=request)
    # The last part of the product name will be the product ID assigned to a
    # product by Google. Product ID has the format
    # `contentLanguage~feedLabel~offerId`
    print(f"Input successful: {response}")
  except RuntimeError as e:
    print("Input failed")
    print(e)
    # After the product is inserted, the product ID will be returned in the
    # response. We recommend that you check the Merchant Center to ensure that
    # the product is approved and visible to users before using the product ID
    # in any downstream processes.


if __name__ == "__main__":
  insert_product_input()

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

  try {
    console.log('Sending insert ProductInput request');
    // Call the ProductInputs.insert API method.
    response = MerchantApiProducts.Accounts.ProductInputs.insert(
        productResource, parent, {dataSource});
    // RESTful insert returns the JSON object as a response.
    console.log('Inserted ProductInput below');
    console.log(response);
  } catch (e) {
    console.log('ERROR!');
    console.log(e);
  }
}

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",
   "availability": "IN_STOCK",
   "price": {
     "amountMicros": "15990000",
     "currencyCode": "USD"
   },
   "condition": "NEW",
   "gtins": ["9780007350896"]
 }
}'

補助データソースを使用して商品情報を追加または更新する

補助データソースを使用して、メイン データソースにすでに存在する商品に追加データまたはオーバーライド データを提供します。これは、元の商品の入力を変更せずに、プロモーションやカスタムラベルなどの情報を追加したり、特定の商品データをオーバーライドしたりする場合に便利です。

これを行うには、productInputs.insert メソッドを使用しますが、dataSource パラメータで補助データソースの name を指定します。offerIdcontentLanguagefeedLabel が既存のプロダクトと一致することを確認します。

この例では、補助データソースを使用して既存の商品にカスタムラベルを追加します。

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"
 }
}

この呼び出しの後、default_rule で定義されているように、フィードルールのメイン データソースよりも補助データソースの優先度が高いと仮定すると、最終的な ProductcustomLabel0clearance-sale に設定されます。

商品を非同期で追加する

多数の商品を効率的にアップロードするには、複数の挿入リクエストを同時に送信します。各リクエストに対するレスポンスを待ってから次のリクエストを送信するのではなく、非同期で送信し、レスポンスが届いたら処理できます。このアプローチにより、データ アップロードのスループットを大幅に改善できます。

次のコードサンプルは、商品を非同期で挿入する方法を示しています。curl を使用して複数のリクエストを同時に実行する方法については、複数のリクエストを同時に送信するをご覧ください。

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.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 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);
  }
}

Python

import asyncio
import functools
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

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


def print_product_input(i, task):
  print("Inserted ProductInput number: ", i)
  # task.result() contains the response from async_insert_product_input
  print(task.result())


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_produc_input request.
  """

  print("Sending insert product input requests")

  try:
    response = await client.insert_product_input(request=request)
    # The response is an async corouting inserting the ProductInput.
    return response
  except RuntimeError as e:
    # Catch and print any exceptions that occur during the API calls.
    print(e)


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 ProductInputsServiceClient.
  client = ProductInputsServiceAsyncClient(credentials=credentials)
  tasks = []
  for i in range(5):
    product_input = _create_random_product()
    request = InsertProductInputRequest(
        parent=_PARENT,
        data_source=data_source_name,
        product_input=product_input,
    )
    # Create the async task
    insert_product_task = asyncio.create_task(
        async_insert_product_input(client, request)
    )
    # Add the callback
    callback_function = functools.partial(print_product_input, i)
    insert_product_task.add_done_callback(callback_function)
    # Add the task to our list
    tasks.append(insert_product_task)

  # Await all tasks to complete concurrently
  # The print_product_input callback will be called for each as it finishes
  await asyncio.gather(*tasks)


if __name__ == "__main__":
  asyncio.run(main())

特定の国を対象とする商品を追加する

商品ごとに国をターゲットに設定して、データソースの設定をオーバーライドしたり、データソースに国が定義されていない場合に商品をターゲットに設定したりできます。

shipping 属性で対象国を指定する

データソースで特定の国が定義されていない場合は、productAttributes 内で shipping フィールドを指定することで、商品の対象国を設定できます。このフィールドでは、さまざまな国の送料とルールを指定し、配信対象として暗黙的に指定できます。

この例では、2 つの別々の shipping エントリを指定することで、米国とカナダをターゲットにしています。

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"
       }
     }
   ]
 }
}

国と掲載先を除外する

データソースがすでに複数の国をターゲットにしている場合は、それらの国の一部または特定の掲載先で商品が表示されないように除外できます。

  • shoppingAdsExcludedCountries 属性を使用すると、特定の商品が特定の国のショッピング広告に表示されないようにすることができます。
  • excludedDestinations 属性を使用すると、FreeListings などの特定のマーケティング方法で商品が表示されなくなります。

この例では、データソースの対象国は米国、カナダ、メキシコであるとします。このリクエストでは、メキシコのショッピング広告と無料リスティングの掲載先から商品が完全に除外されます。

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.get メソッドを使用します。このメソッドは Product リソースを返します。このリソースには、フィードのルールと補助データソースが適用された後のすべての productAttributes と、現在の検証ステータスが含まれます。

ProductInput を挿入または更新してから、最終的な Product を取得できるようになるまでに、数分の遅延が生じることがあります。

GET https://merchantapi.googleapis.com/products/v1/accounts/{ACCOUNT_ID}/products/en~US~SKU12345

リクエストが成功すると、Product リソースが返されます。

{
 "name": "accounts/{ACCOUNT_ID}/products/en~US~SKU12345",
 "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"
 }
}

次のコードサンプルは、商品を取得する方法を示しています。

Java

import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.GoogleCredentials;
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 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 {

  public static void getProduct(Config config, String product) 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();

    // Calls the API and catches and prints any network failures/errors.
    try (ProductsServiceClient productsServiceClient =
        ProductsServiceClient.create(productsServiceSettings)) {

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

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

      System.out.println("Retrieved Product below");
      System.out.println(response);
    } catch (Exception e) {
      System.out.println(e);
    }
  }

  public static void main(String[] args) throws Exception {
    Config config = Config.load();
    // 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 product = "accounts/{datasource}/products/{productId}";

    getProduct(config, product);
  }
}

PHP

use Google\ApiCore\ApiException;
use Google\Shopping\Merchant\Products\V1\GetProductRequest;
use Google\Shopping\Merchant\Products\V1\Product;
use Google\Shopping\Merchant\Products\V1\Client\ProductsServiceClient;


/**
 * This class demonstrates how to get a single product for a given Merchant Center
 * account.
 */
class GetProduct
{

    // ENSURE you fill in the product ID for the sample to work.
    private const PRODUCT = 'INSERT_PRODUCT_ID_HERE';

    /**
     * Gets a product from 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 get.
     *      Format: `accounts/{account}/products/{productId}`.
     *
     * @return void
     */
    public static function getProductSample($config, $product) : 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);

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

            // Sends the request.
            echo "Sending get Product request\n";
            $response = $productsServiceClient->getProduct($request);

            // Outputs the response.
            echo "Retrieved Product below\n";
            print_r($response);
        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }

    /**
     * Helper to execute the sample.
     *
     * @return void
     */
    public function callSample(): void
    {
        $config = Config::generateConfig();
        // Identifies the product to retrieve.
        // The name has the format: accounts/{account}/products/{productId}.
        $product = ProductsServiceClient::productName(
            $config['accountId'],
            self::PRODUCT
        );

        self::getProductSample($config, $product);
    }

}


$sample = new GetProduct();
$sample->callSample();

Python

from examples.authentication import configuration
from examples.authentication import generate_user_credentials
from google.shopping import merchant_products_v1


_ACCOUNT = configuration.Configuration().read_merchant_info()

# ENSURE you fill in the product ID for the sample to
# work.
# In the format of `contentLanguage~feedLabel~offerId`
_PRODUCT = "[INSERT_PRODUCT_HERE]"
_NAME = f"accounts/{_ACCOUNT}/products/{_PRODUCT}"


def get_product():
  """Gets the specified `Product` resource."""

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

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

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

  # Makes the request and catches and prints any error messages.
  try:
    response = client.get_product(request=request)
    print(f"Get successful: {response}")
  except RuntimeError as e:
    print("Get failed")
    print(e)


if __name__ == "__main__":
  get_product()

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:
  // channel~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.
    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/en~US~SKU12345" \
-H "Authorization: Bearer <API_TOKEN>"

データソースから商品を削除する

特定のデータソースから商品入力を削除するには、productInputs.delete メソッドを使用します。

  • メインのデータソースから削除すると、補助の ProductInputs を含む商品全体が Merchant Center から削除されます。
  • 補助データソースから削除した場合、そのデータソースの属性のみが商品から削除されます。商品自体は、メイン データソースと他の補助データソースから取得され、残ります。

商品入力を削除する dataSource を指定する必要があります。

DELETE https://merchantapi.googleapis.com/products/v1/accounts/{ACCOUNT_ID}/productInputs/en~US~SKU12345?dataSource=accounts/{ACCOUNT_ID}/dataSources/{DATASOURCE_ID}

呼び出しが成功すると、空のレスポンスが返されます。

次のコードサンプルは、商品を削除する方法を示しています。

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.");
    } catch (Exception e) {
      System.out.println(e);
    }
  }

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


_ACCOUNT = 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`
_PRODUCT = "[INSERT_PRODUCT_HERE]"
_DATA_SOURCE = "[INSERT_DATA_SOURCE_HERE]"
_NAME = f"accounts/{_ACCOUNT}/productInputs/{_PRODUCT}"
_DATA_SOURCE_NAME = f"accounts/{_ACCOUNT}/dataSources/{_DATA_SOURCE}"


def delete_product_input():
  """Deletes the specified `ProductInput` resource."""

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

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

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

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


if __name__ == "__main__":
  delete_product_input()

cURL

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