コンテンツにスキップ
ShopentlyLearn

更新 — 商品

「商品」の指定したデータを変更します。必須フィールドと書き込みの規則は以下に記載しています。

PATCH/api/v1/products/{product_id}

認証
Bearer
利用可能な最低プラン
STANDARD
必要な権限
catalog.product:update
特定のフィールドに必要な追加権限
  • variant_value_ids を指定した場合: catalog.product_variant:create, catalog.product_variant:update, catalog.product_variant:delete
  • variants_enabled を指定した場合: catalog.product_variant:update
詳細を見る

コード例

リクエスト例

cURL

: "${SHOPENTLY_SHOP_URL:?}"

curl --disable --globoff --fail-with-body --silent --show-error \
  --proto '=https' --connect-timeout 5 --max-time 30 \
  --request PATCH \
  --header "Authorization: Bearer ${SHOPENTLY_ACCESS_TOKEN:?}" \
  --header 'Idempotency-Key: example-update_product_api_v1_products__product_id__patch' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "expected_version": 1,
  "sku": "example"
}' \
  --url "${SHOPENTLY_SHOP_URL%/}"/api/v1/products/example

PHP

<?php
declare(strict_types=1);

$baseUrl = rtrim(getenv('SHOPENTLY_SHOP_URL') ?: '', '/');
$origin = parse_url($baseUrl);
if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
    isset($origin['user'], $origin['pass']) || isset($origin['user']) || isset($origin['path']) ||
    isset($origin['query']) || isset($origin['fragment'])) {
    throw new RuntimeException('SHOPENTLY_SHOP_URL must be an HTTPS origin');
}
$credential = getenv('SHOPENTLY_ACCESS_TOKEN') ?: '';
if ($credential === '' || strpbrk($credential, "\r\n") !== false) {
    throw new RuntimeException('Invalid credential');
}
$headers = ['Idempotency-Key: example-update_product_api_v1_products__product_id__patch', 'Content-Type: application/json'];
$headers[] = 'Authorization: Bearer ' . $credential;
$responseHeaders = [];
$handle = curl_init($baseUrl . '/api/v1/products/example');
curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HEADERFUNCTION => static function ($handle, string $line) use (&$responseHeaders): int {
        if (str_contains($line, ':')) {
            [$name, $value] = explode(':', $line, 2);
            $responseHeaders[strtolower(trim($name))] = trim($value);
        }
        return strlen($line);
    },
]);
curl_setopt($handle, CURLOPT_POSTFIELDS, '{
  "expected_version": 1,
  "sku": "example"
}');
$raw = curl_exec($handle);
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
$error = curl_error($handle);
curl_close($handle);
if ($raw === false) {
    throw new RuntimeException('Transport error: ' . $error);
}
if ($status < 200 || $status >= 300) {
    throw new RuntimeException('HTTP ' . $status . '; request_id=' .
        ($responseHeaders['x-request-id'] ?? '') . '; retry_after=' . ($responseHeaders['retry-after'] ?? ''));
}
$result = $raw === '' ? null : json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
echo json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), PHP_EOL;

Python

from contextlib import ExitStack
import json
import os
from urllib.parse import urlsplit

import requests  # python -m pip install requests

base_url = os.environ["SHOPENTLY_SHOP_URL"].rstrip("/")
origin = urlsplit(base_url)
if origin.scheme != "https" or not origin.hostname or origin.username or origin.password or origin.path or origin.query or origin.fragment:
    raise ValueError("SHOPENTLY_SHOP_URL must be an HTTPS origin")
credential = os.environ['SHOPENTLY_ACCESS_TOKEN']
if not credential or "\r" in credential or "\n" in credential:
    raise ValueError("Invalid credential")
headers = {'Idempotency-Key': 'example-update_product_api_v1_products__product_id__patch'}
headers["Authorization"] = 'Bearer' + " " + credential
body = '{\n  "expected_version": 1,\n  "sku": "example"\n}'
if body is not None:
    headers["Content-Type"] = "application/json"

with ExitStack() as stack:
    session = stack.enter_context(requests.Session())
    files = None
    if False:
        image = stack.enter_context(open(os.environ["SHOPENTLY_IMAGE_FILE"], "rb"))
        if os.fstat(image.fileno()).st_size > 5242880:
            raise ValueError("Image exceeds 5 MiB")
        files = {"file": ("article.png", image, "image/png")}
    response = session.request(
        'PATCH', base_url + '/api/v1/products/example', headers=headers,
        data=body.encode("utf-8") if body is not None else None, files=files,
        timeout=(5, 30), allow_redirects=False,
    )
    if not 200 <= response.status_code < 300:
        raise RuntimeError(f"HTTP {response.status_code}; request_id={response.headers.get('X-Request-ID', '')}; retry_after={response.headers.get('Retry-After', '')}")
    if False:
        if response.headers.get("Content-Type") != "image/webp" or len(response.content) > 5242880:
            raise ValueError("Unexpected image response")
        with open(os.environ["SHOPENTLY_OUTPUT_FILE"], "xb") as output:
            output.write(response.content)
    else:
        result = response.json() if response.content else None
        print(json.dumps(result, ensure_ascii=False, indent=2))

レスポンス例 200

JSON
{
  "data": {
    "deleted": false,
    "id": "example",
    "version": 1
  },
  "meta": {
    "request_id": "example"
  }
}

仕組み

  • プラン制限でブロックされた商品を除き、下書きを含む未削除の商品を取得できます。
  • ブロックされた商品は一覧から除外され、直接アクセスすると entitlements.resource_blocked が返ります。
  • そのため API での可視性はストアでの公開状態とは異なります。
  • ブランド、カテゴリー、属性、メディアには既存の識別子を使います。
  • 価格には通貨、価格表、税モード、同時更新制御用のバージョンを明示します。
  • 商品価格とバリエーション価格は別です。
  • 対応する価格構造と正確な10進数文字列を使用してください。

パラメーター

パスパラメーター

product_id文字列必須

商品識別子。

制約
pattern
"^[a-z0-9][a-z0-9_-]{0,62}$"
例: "example"

クエリパラメーター

locale文字列 null任意

このリクエストで選択した言語。

文字列:minLength: 2maxLength: 20
market_id文字列 null任意

コンテキストエンドポイントが返した市場識別子。

制約 · 文字列 1
pattern
"^[a-z0-9][a-z0-9_-]{0,62}$"
market_code文字列 null任意

コンテキストエンドポイントが返した市場コード。

文字列:minLength: 1maxLength: 16

リクエストヘッダー

Idempotency-Key文字列必須

1つの論理的な書き込み操作の一意な識別子。

minLength: 1maxLength: 128
制約
pattern
"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
例: "example-update_product_api_v1_products__product_id__patch"

リクエスト本文

application/json · 必須

ProductUpdate
brand_id文字列 null任意

ブランド識別子。

文字列:minLength: 1maxLength: 63
condition文字列任意

商品の状態。

制約
enum
["new", "refurbished", "used", "returned", "display", "damaged"]
description文字列 null任意

選択した言語での説明。

文字列:maxLength: 100000
例: null
expected_variant_price_versionsオブジェクト追加フィールド: 整数
minimum: 0maximum: 9223372036854775807
任意

事前に取得した価格バージョン。キーはバリエーション識別子です。

maxProperties: 250
制約
propertyNames
{"maxLength": 63, "minLength": 1}
expected_version整数必須

書き込み前に取得したバージョン。不一致の場合は更新を拒否します。

minimum: 1maximum: 9223372036854775807
例: 1
feature_value_ids配列 <文字列> 任意

選択した属性値の識別子。

maxItems: 1000
配列:minLength: 1maxLength: 63
identifiersProductIdentifiers null任意

商品識別コード。

market_ids配列 <文字列> 任意

選択した市場の識別子。

maxItems: 1000
配列:minLength: 1maxLength: 63
media_ids配列 <文字列> 任意

既存のメディアアセット識別子。

maxItems: 20
配列:minLength: 1maxLength: 63
meta_description文字列 null任意

選択した言語での検索エンジン向け説明。

文字列:maxLength: 160
meta_keywords配列 <文字列> null任意

選択した言語での検索エンジン向けキーワード。

配列:maxItems: 20
配列 / 配列:maxLength: 50
meta_title文字列 null任意

選択した言語での検索エンジン向けタイトル。

文字列:maxLength: 70
name文字列 null任意

該当コンテキストでのリソース名。

文字列:maxLength: 300
例: null
pricingProductPrice 任意

価格設定と同時更新制御用のバージョン。

例: {"amount":"0.00","currency":"PLN","expected_price_list_version":1,"expected_version":0,"kind":"base","price_list_id":"example","tax_included":true}
primary_category_id文字列 null任意

主要カテゴリー識別子。

文字列:minLength: 1maxLength: 63
published真偽値任意

リソースが公開済みかどうか。

sku文字列任意

在庫管理単位のコード。

minLength: 1maxLength: 100
制約
pattern
"^\\S+$"
例: "example"
slug文字列 null任意

選択した言語での URL パス部分。

文字列:maxLength: 160
tax_category_id文字列 null任意

税カテゴリー識別子。

文字列:minLength: 1maxLength: 63
variant_value_ids配列 <文字列> 任意

選択したバリエーション値の識別子。

maxItems: 250
配列:minLength: 1maxLength: 63
variants_enabled真偽値任意

商品がバリエーションを使うかどうか。

レスポンス

レスポンス 200

dataMutationData 必須

成功レスポンスのデータ。

フィールド · data
deleted真偽値必須

削除が適用されたかどうか。

id文字列必須

リソース識別子。返された値をそのまま保持してください。

version整数 null必須

同時更新制御に使うリソースバージョン。

metaContextPageMeta ContextMeta PageMeta RequestMeta 必須

レスポンスのメタデータ。

フィールド · meta

ContextPageMeta

locale文字列必須

このリクエストで選択した言語。

market_code文字列必須

コンテキストエンドポイントが返した市場コード。

market_id文字列必須

コンテキストエンドポイントが返した市場識別子。

paginationPagination 必須

カーソルによるページ分割のメタデータ。

request_id文字列必須

申請識別子。レスポンスのメタデータでは HTTP リクエストを識別します。

minLength: 1maxLength: 100

ContextMeta

locale文字列必須

このリクエストで選択した言語。

market_code文字列必須

コンテキストエンドポイントが返した市場コード。

market_id文字列必須

コンテキストエンドポイントが返した市場識別子。

request_id文字列必須

申請識別子。レスポンスのメタデータでは HTTP リクエストを識別します。

minLength: 1maxLength: 100

PageMeta

paginationPagination 必須

カーソルによるページ分割のメタデータ。

request_id文字列必須

申請識別子。レスポンスのメタデータでは HTTP リクエストを識別します。

minLength: 1maxLength: 100

RequestMeta

request_id文字列必須

申請識別子。レスポンスのメタデータでは HTTP リクエストを識別します。

minLength: 1maxLength: 100
レスポンス例

HTTP ステータス

200
成功 スキーマ
400
無効なリクエスト
401
認証が必要です
403
権限がありません
404
リソースが見つかりません
409
バージョンの競合
422
検証エラー
429
リクエスト上限を超過
その他の HTTP ステータス (7)
405
許可されていないメソッド
413
リクエストが大きすぎます
415
未対応の形式
500
サーバーエラー
502
API ゲートウェイエラー
503
サービスを利用できません
504
タイムアウト

エラー一覧: ErrorResponse

ヘッダー

401

WWW-Authenticate文字列任意

サーバーが要求する認証方式。

405

Allow文字列任意

このリソースが対応する HTTP メソッド。

429, 503

Retry-After整数任意

再試行までに最低限待つ秒数。

minimum: 0