Skip to content

検索API v1 ​

  • 利用条件: Pro プラン以上

まるっと検索は通常、検索結果ページの HTML をアプリ側で描画します。検索APIでは、アプリは何も描画せず、検索結果の JSON だけをテーマに渡します。

  • 既存テーマのデザイン・レイアウトには一切触れません
  • 日本語の形態素解析・表記ゆれ吸収・同義語・絞り込みのロジックだけを利用できます
  • 描画は御社のコードで実装していただく必要があります

1. 検索の実行 ​

まるっと検索は JSON を返すだけです。呼び出し・状態管理・描画はすべて御社の実装になります。こちらから JavaScript を読み込ませることはしません。

GET /apps/marutto-search/search?type=search&contract=v1&q=ワンピース&limit=24&offset=0
  • ストア自身のドメイン上の URL です(Shopify App Proxy)。API キーも CORS 設定も不要で、ショップは Shopify の署名から自動で特定されます。
  • contract=v1 は、本仕様の形でレスポンスを返すよう求める指定です。これを付けたリクエストにだけ、本ドキュメントに書いた形を保証します。付けない場合はアプリ内部のレスポンスが返り、その形は予告なく変わります。アプリ内部のレスポンスは、まるっと検索のウィジェットが読むための形で、仕様ではありません。
  • contract=v1 は Pro プラン以上で利用できます。Pro プラン未満(Free・Basic)の場合は 403 と {"code": "plan_required"} を返します。
  • v1 が定めるのは、確定検索(type=search)と、商品のレビューの一覧(type=reviews、10章)のレスポンスです。suggest(入力補完)に contract=v1 を付けた場合は 400 と {"code": "unsupported_contract_type"} を返します。

クエリパラメータ ​

パラメータデフォルト内容
contract—v1。必須(本仕様の形を求める指定)
type—search(確定検索)。必須。レビューの一覧は reviews(10章)
q—検索語。必須(全件ブラウズは *)
session_id—セッション識別子。必須(7章)
limit8取得件数。上限 50
offset0開始位置。上限 2,000(超えた値は 2,000 に丸められます)
sortrelevancerelevance / price_asc / price_desc / newest / rating
localeストア設定ja / en など。多言語ストアで解析言語を切り替える
filter_vendor—ベンダー名
filter_type—商品タイプ
filter_tags—タグ(複数はカンマ区切り。AND 条件)
filter_price_min / filter_price_max—価格帯
filter_available—true / false(在庫あり)
filter_on_sale—true / false(セール中)
filter_content_type—product / article / page
filter_collection—コレクションのハンドル
filter_option_name / filter_option_value—バリエーション(例: カラー / ベージュ)
filter_metafield—メタフィールド(namespace.key:値)。繰り返し指定可
filter_rating—評価 1〜5。★N以上の商品だけ(下記)

sort=rating は評価の高い順です。レビューの少ない商品が平均だけで上に来すぎないよう、件数も加味して並べます。評価の無い商品は最後になります。

filter_rating=4 は「★4以上」です。商品の評価(rating.average)を四捨五入した星の数で判定し、評価の無い商品と記事・ページは外れます。1〜5 の整数以外は無視されます。

縮退している間(4章)は、評価順・新しい順・評価の絞り込みは効きません(結果は関連度か価格の順です)。sort はそのときもリクエストの値をそのまま返します(古い応答を捨てる判定のため)。

メタフィールドで絞り込むときは、filter_metafield を繰り返し指定します。値の形式は namespace.key:値 で、最初の : で分割されます。キーに : は含まれないため、値に : があってもそのまま扱えます。項目をまたいだ指定は AND になります。

&filter_metafield=custom.material:リネン&filter_metafield=custom.origin:日本

対象になるのは、管理画面で「絞り込みに使う」を有効にしたメタフィールドだけです(テキスト型・リスト型、最大10個)。

管理画面で「絞り込みに使う」を有効にしていないメタフィールドを filter_metafield に渡しても、エラーにはならず、その指定は無視されます(全件が返ります)。設定を切り替えたあとも古い URL が共有され続けるため、残ったまま解除できない状態を避ける仕様です。開発中にキー名を打ち間違えると全件が返るので、まずファセットに出ている name と一致しているかご確認ください。

全件ブラウズ(q=*)は商品テーブルだけを読む別経路のため、できないことがあります。通常の検索語では動く次の2点が、q=* のときだけ変わります。

指定q=* のときの挙動
filter_content_type=article / page常に0件(記事・ページはブラウズに含まれません)
facets.tags / content_types / variant_options常に [](キーは存在します)

上記以外のフィルタと filter_metafield は、検索語のときと同じく効きます。

記事・ページを一覧したい場合は、検索語を伴う type=search をご利用ください。

エラーの返り方 ​

状況ステータスcode
q が無い400query_required
q が200文字を超える400query_too_long
type が search / suggest / preview 以外400invalid_type
contract が v1 以外400unsupported_contract
contract=v1 を search / reviews 以外に付けた400unsupported_contract_type
プランが Pro 未満403plan_required
検索基盤の障害503本文は通常の形(degraded: true)

リクエストの誤り(4xx)では { error, code } を返します(unsupported_contract のときは、対応している版の一覧 supported も付きます)。空の検索結果として返すと、total: 0 に見えて原因が分からなくなるためです。code で分岐してください。

検索基盤側の問題(503)では、通常のレスポンスと同じ形で返します。障害に重ねて data.results で例外まで起こさないためです。

参照実装 ​

そのまま動く最小構成を search-api-sample.js に置いています。描画は御社のデザインに書き換えていただく前提ですが、書き換えないでいただきたい箇所が3つあります。

  1. contract=v1 を必ず付ける(本仕様の形を求める指定)
  2. degraded を見る(新規ストアは導入初日に必ずこの経路を通ります)
  3. 2か所の Shopify.analytics.publish(実装しないと検索経由売上が計上されません)

呼び出し側でお願いしたいこと ​

まるっと検索側の JS が無いため、以下は御社の実装に委ねられます。

  • 入力補完のデバウンス(推奨 250〜300ms)。デバウンスせずに type=suggest を送ると、レート制限に達します
  • 応答の順序の保証。速く入力すると応答が前後します。レスポンスの query / offset / limit / sort はリクエスト値のエコーバックなので、古い応答を捨てる判定に使えます
  • 同一条件の連続リクエストの抑制

2. ページング ​

フィールド用途
total全ヒット件数
offsetこのレスポンスが何件目から始まっているか
limitこのレスポンスの1ページあたり件数
js
// 「もっと見る」/無限スクロール
const MAX_OFFSET = 2000; // 1章のとおり、これを超える指定は 2000 に丸められます
const next = data.offset + data.results.length;
if (next < data.total && next < MAX_OFFSET) {
  // offset=next で再取得し、既存リストに追加する
}

next < data.total だけで判定しないでください。 offset の上限に達したあともサーバは同じページを返すため、total が上限より大きいと「もっと見る」が消えず、同じ商品が何度も追加され続けます。上限との比較を必ず入れてください。

ページ番号ナビ・「もっと見る」・無限スクロールのいずれも、offset と limit の指定だけで実現できます。

注意: offset 方式のため、ページを送っている最中に商品の追加・削除・在庫変動で索引が更新されると、同じ商品が2回出たり、1件飛ばされたりすることがあります。無限スクロールで目立ちやすい挙動です。厳密な一貫性が必要な場合はご相談ください。

3. レート制限 ​

不正利用対策のため、10秒あたりのリクエスト数に上限があります。超えると HTTP 429 を返します。

種別ショップ単位訪問者単位全ストア合計
type=search50 / 10秒20 / 10秒125 / 10秒
type=suggest150 / 10秒40 / 10秒150 / 10秒
type=reviews100 / 10秒30 / 10秒150 / 10秒

type=reviews の上限は、まるっと検索のウィジェットが読む人気の検索語・おすすめなどと共有の枠です。同じ訪問者のこれらの呼び出しと合わせて数えます。

通常の買い物客の操作でこの上限に当たることはありませんが、デバウンスなしで入力補完を呼ぶと、訪問者単位の上限に容易に達します。429 を受けた場合は、時間をおいて再試行するか、前回の結果を表示したままにする実装を推奨します。

まるっと検索は単一のインスタンスで全ストアを処理しています。そのため、ショップ単位の上限内であっても、同時刻の他ストアの負荷と合わさって「全ストア合計」の上限に達すると、429 になることがあります。自社のリクエスト数だけからは予測できない 429 があり得るということです。実運用でこの合計に達したことはありませんが、仕様として明記しておきます。負荷試験など短時間に大量のリクエストを送る予定がある場合は、事前にご連絡ください。

4. レスポンスの保証 ​

トップレベルのキーは常に存在します ​

結果が0件でも、内部でエラーが起きていても、results / facets / total などのキーが省略されることはありません(results は []、total は 0 になり、facets は中のキーまですべて揃います)。data.facets.tags.forEach(...) のような記述が、状況によって例外になることはありません。

一時的に検索が縮退することがあります ​

インストール直後の初回同期中、多言語ストアの翻訳同期中など、まるっと検索の索引がまだ使えない期間があります。この間は degraded が true になり、結果が空または Shopify 標準検索の結果になります。

json
{
  "schemaVersion": "v1",
  "degraded": true,
  "degraded_reason": "paid_sync_pending",
  "total": 0,
  "results": [],
  "facets": {
    "vendors": [],
    "product_types": [],
    "tags": [],
    "content_types": [],
    "variant_options": [],
    "metafields": [],
    "ratings": [],
    "price_range": null,
    "on_sale_count": 0
  }
}
js
if (data.degraded) {
  // 例: 「検索を準備しています」を出す、Shopify標準の検索にフォールバックする 等
}

導入初日に必ず通る経路です。 新規ストアはインストール後しばらく縮退した状態を返すため、degraded を見ていない実装は「入れた直後は動かない」ように見えます。必ずご対応ください。

degraded_reason は原因の識別用で、値の集合は将来増えます。分岐条件には degraded(真偽値)をお使いいただき、degraded_reason は表示やログの用途にとどめてください。

5. レスポンス例 ​

検索語「ワンピース」でアパレルストアを検索した場合の例です。値はすべてサンプルです。

json
{
  "schemaVersion": "v1",
  "degraded": false,
  "degraded_reason": null,
  "query": "ワンピース",
  "query_id": "q_8f3c1ad24b7e",
  "measurement_proof": "mp_5c19e0b7a3d84f26",
  "total": 137,
  "limit": 24,
  "offset": 0,
  "sort": "relevance",
  "redirect_url": null,

  "results": [
    {
      "content_type": "product",
      "product_id": "gid://shopify/Product/8123456789",
      "title": "リネンブレンド ロングワンピース",
      "handle": "linen-blend-long-dress",
      "url": "/products/linen-blend-long-dress",
      "image_url": "https://cdn.shopify.com/s/files/1/0001/0002/products/linen-dress.jpg",
      "price_min": 12800,
      "price_max": 12800,
      "compare_at_price": 16000,
      "available": true,
      "tags": ["春夏", "洗える", "新作"],
      "metafields": [
        { "name": "custom.material", "label": "素材", "values": ["リネン"] }
      ],
      "rating": { "average": 4.6, "count": 23, "source": "marutto" },

      "variants": [
        {
          "id": "44001",
          "title": "ベージュ / M",
          "image_url": "https://cdn.shopify.com/s/files/1/0001/0002/products/linen-dress-beige.jpg",
          "price": 12800,
          "compare_at_price": 16000,
          "available": true,
          "options": [
            { "name": "カラー", "value": "ベージュ" },
            { "name": "サイズ", "value": "M" }
          ]
        },
        {
          "id": "44002",
          "title": "ベージュ / L",
          "price": 12800,
          "available": false,
          "options": [
            { "name": "カラー", "value": "ベージュ" },
            { "name": "サイズ", "value": "L" }
          ]
        }
      ]
    },
    {
      "content_type": "article",
      "product_id": "gid://shopify/Article/5500112233",
      "title": "ワンピースの着回しコーデ 12選",
      "handle": "one-piece-coordinate",
      "url": "/blogs/journal/one-piece-coordinate",
      "image_url": "https://cdn.shopify.com/s/files/1/0001/0002/articles/coordinate.jpg",
      "tags": ["コーディネート"],
      "excerpt": "一枚で決まるワンピースを、季節ごとに着回すコツをご紹介します。",
      "published_at": "2026-04-18T09:00:00Z",
      "author": "スタッフ 田中",
      "blog_handle": "journal"
    }
  ],

  "facets": {
    "vendors": [
      { "value": "ATELIER NOA", "count": 42 },
      { "value": "LUMIÈRE", "count": 31 }
    ],
    "product_types": [{ "value": "ワンピース", "count": 118 }],
    "tags": [
      { "value": "春夏", "count": 64 },
      { "value": "洗える", "count": 38 }
    ],
    "content_types": [
      { "value": "product", "count": 131 },
      { "value": "article", "count": 5 }
    ],
    "variant_options": [
      {
        "name": "カラー",
        "values": [
          { "value": "ベージュ", "count": 57 },
          { "value": "ブラック", "count": 49 }
        ]
      }
    ],
    "metafields": [
      {
        "name": "custom.material",
        "label": "素材",
        "values": [{ "value": "リネン", "count": 42 }]
      }
    ],
    "ratings": [
      { "value": "4", "count": 96 },
      { "value": "3", "count": 104 }
    ],
    "price_range": [3900, 42000],
    "on_sale_count": 18
  },

  "suggestions": [],
  "related_keywords": []
}

6. フィールド定義 ​

トップレベル ​

フィールド型内容
schemaVersionstringデータ構造のバージョン。初版は "v1"
degradedboolean索引が一時的に使えない状態か(→ 4章)。常に存在します
degraded_reasonstring | null縮退の原因。degraded が false のときは null(キーは常に存在)
querystring検索語(入力そのまま)
query_idstringこの検索の識別子。クリックをこれで紐付けます(→ 7章)
measurement_proofstring売上紐付け用の署名。検索経由売上の計測に必須(→ 7章)
totalnumberヒット総件数(ページングされる前の全件)
limitnumberこのレスポンスの1ページあたり件数
offsetnumberこのレスポンスが何件目から始まっているか
sortstringrelevance / price_asc / price_desc / newest / rating
redirect_urlstring | null管理画面の検索リダイレクトで設定した遷移先。値がある場合は結果を描画せず遷移する想定
resultsarray検索結果(商品・記事・ページが混在。content_type で判別)
facetsobject絞り込み用の集計値(下記)
suggestionsstring[]0件時の候補語。該当なしは空配列(キーは常に存在)
related_keywordsstring[]0件時に、他の購入者が続けて検索した語。それ以外は空配列(キーは常に存在)

related_keywords が入るのは、結果が0件で、コレクション指定(filter_collection)も検索リダイレクトもない検索だけです。結果が1件以上ある検索では、常に空配列です。

ここに載っていないフィールドは返りません。内部のスコアやレビュー照合の詳細は、改善のたびに変わるため公開対象から外しています。results は並び替え済みで届きます。

facets ​

フィールド型内容
vendorsarrayベンダー別の件数。常に存在(該当なしは [])
product_typesarray商品タイプ別。常に存在
tagsarrayタグ別。常に存在
content_typesarray種別(product / article / page)別。常に存在
variant_optionsarrayバリエーション軸別(カラー・サイズ等)。常に存在
metafieldsarrayメタフィールド別。常に存在
ratingsarray評価の「★N以上」別(下記)。常に存在
price_rangenumber[] | null価格の下限・上限。該当なしは null([0, 0] は返しません)
on_sale_countnumberセール中の件数。該当なしは 0

facets.metafields[] は { name, label?, values[] } です。name は namespace.key、label は管理画面で設定した見出しで、未設定なら省略されます。表示には label ?? name をお使いください。

facets.ratings[] は { value, count } で、value は "4"(★4以上)と "3"(★3以上)です。件数は★N以上の累積で、★4以上の商品は★3以上にも数えます。0件の行と、上の行と件数が同じ行は省きます(選んでも結果が変わらないため)。value はそのまま filter_rating に渡せます。評価が1件も無いストアでは [] です。

並び順は件数の降順、同数のときは値の昇順です。同じ検索・同じカタログなら常に同じ順で返ります。各グループは上位20件までです。

件数は、絞り込む前の「検索語に一致した全体」で数えます。 検索語を指定した検索(q が * 以外)では、filter_* を付けても vendors・product_types・tags・variant_options・metafields・price_range・on_sale_count は変わりません。選んでいない値の件数も残ります。絞り込んだ後の件数には total をお使いください。

絞り込みが1つだけなら、選んでいない値の件数が「その値に切り替えると何件になるか」です。絞り込みを2つ以上組み合わせたときは、ほかの絞り込みも外した件数なので、切り替えた後の件数とは一致しません。

  • content_types だけは、filter_content_type 以外の絞り込みを反映した件数です(商品・記事・ページのタブの件数が、選んだタブ以外0にならないように)
  • filter_collection を付けると、そのコレクションに限定した「キーワード別の表示ルール」(上位に固定・非表示)が効くので、件数もそれに合わせて変わります
  • 全件ブラウズ(q=*)では、すべてのグループが絞り込み後の件数になります

results[](共通) ​

フィールド型内容
content_typestringproduct / article / page
product_idstringShopify の GID。種別を問わずこのキー名です(歴史的経緯)
titlestringタイトル
handlestringハンドル
urlstringストア内の相対 URL
image_urlstring?画像 URL(無い場合あり)
tagsstring[]Shopify のタグ。付いていない場合は空配列(キーは常に存在)

results[](content_type: "product" のみ) ​

フィールド型内容
price_min / price_maxnumber価格帯(下記)
compare_at_pricenumber | null参考価格(セール表示用)
availableboolean購入可能な在庫があるか
variants[]array?バリエーション。id は数値の ID(下記)
metafields[]arrayメタフィールドの値(下記)。値が無い商品は [](キーは常に存在)
ratingobject | null評価(下記)。評価が無い商品は null(キーは常に存在)

metafields[] は { name, label?, values[] } です。name は namespace.key、label は管理画面で設定した見出しで、未設定なら省略されます(facets.metafields[] と同じ)。values は件数を持たない値の文字列の配列です(例: ["愛媛県"])。facets.metafields[] の values のような { value, count } ではありません。

  • 載るのは、管理画面で「絞り込みに使う」を有効にしたメタフィールドのうち、その商品に値があるものだけです。「検索に使う」だけのメタフィールドは載りません
  • 並びは管理画面に並べた順、values は値の昇順です。リスト型(list.single_line_text_field)は、1つの name に複数の値が入ります
  • 値は翻訳されません。多言語のストアでも、Shopify に保存されている元の値のままです
  • 縮退している間(degraded: true。結果が空か Shopify 標準検索の結果のとき)は、どの商品も [] です

rating は { average, count, source } です。average は平均(小数2桁)、count はレビューの件数です。星の数(★N以上の判定)は average を四捨五入した整数です。

source評価の出どころ
maruttoまるっと検索に集まったレビュー。type=reviews で一覧を取得できます
externalほかのレビューアプリが Shopify の標準の評価(reviews.rating)に書いた評価。レビューの本文はまるっと検索に無いため、一覧は取得できません
  • まるっと検索のレビューが1件でもある商品は marutto です。2つを合算はしません
  • 縮退している間は、どの商品も null です

価格は「ショップの基準通貨」です。税抜/税込は Shopify の設定に従います。

Shopify Markets で多通貨展開されている場合、買い物客が実際に見ている通貨とは一致しません。多通貨対応のご要望があればお知らせください。

多通貨ストアでの判定方法 ​

為替レートを掛けて換算しないでください。 Shopify Markets の価格は為替の単純な換算で決まるものではなく、国ごとの価格リストで個別に設定でき、丸め方も指定できます。掛け算で出した値は「正しそうに見える間違った価格」になり、価格を出さないより悪い結果になります。

テーマでは基準通貨と表示通貨の両方が分かるので、一致するかどうかだけを確認してください。

js
// Liquid でストアの基準通貨を埋め込む
const shopCurrency = "{{ shop.currency }}"; // 例: JPY(このAPIが返す通貨)
const displayCurrency = Shopify.currency.active; // 例: USD(買い物客が見る通貨)

if (shopCurrency === displayCurrency) {
  render(item.price_min); // そのまま描画してよい
} else {
  // 価格は描画しない、または Storefront API から取得する
}

単一通貨のストア(多くの場合こちら)では両者が常に一致するため、この分岐は else に入りません。

variants[].id は数値の ID です。商品の product_id が gid://shopify/Product/… 形式なのに対し、バリエーションの ID は "44001" のような数値の文字列です。Shopify 自身が次の2か所でこの形を求めるためです。

  • 商品ページでバリエーションを選ばせる: /products/xxx?variant=44001
  • カートに入れる(Cart AJAX API): { "id": 44001, "quantity": 1 }

どちらも GID を渡すとエラーにならずに無視されます(選択されないまま商品ページが開きます)。GID が必要な場合は "gid://shopify/ProductVariant/" + variant.id で組み立ててください。

results[](content_type: "article" / "page" のみ) ​

フィールド型内容
excerptstring?本文の抜粋
published_atstring?公開日時(ISO 8601)
authorstring?著者(記事のみ)
blog_handlestring?所属ブログのハンドル(記事のみ)

7. 計測 ​

まるっと検索が商品カードを描画しないため、何も実装しないと管理画面の検索分析が機能しません。影響するのはクリック率・クリックされた商品ランキング・検索経由売上です。

計測は Shopify の Web Pixel 経由で行うため、御社で実装していただくのはイベントの publish だけです(HTTP リクエストはまるっと検索の Pixel が行います)。

検索リクエストに session_id を付けてください(必須) ​

このエンドポイントは URL を知っていれば誰でも呼び出せるため、session_id の無いリクエストは買い物客の検索ではないと判断し、検索分析に計上しません(検索結果は通常どおり返ります)。付け忘れると、検索は動くのに管理画面の分析が空のままになります。

session_id は御社で発番していただく任意の文字列です。同一セッション内で同じ値であれば、形式は問いません。

1) 検索したとき ​

js
Shopify.analytics.publish("marutto_search", {
  query: data.query,
  query_id: data.query_id,
  session_id: sessionId, // 検索リクエストに付けたものと同じ値
  measurement_proof: data.measurement_proof,
  result_count: data.total,
});

2) 検索結果をクリックしたとき ​

js
Shopify.analytics.publish("marutto_result_clicked", {
  query_id: data.query_id,
  session_id: sessionId,
  measurement_proof: data.measurement_proof,
  product_id: item.product_id,
  position: index, // 0 始まり
});

売上の紐付けに使う measurement_proof ​

検索経由売上の紐付けは、この値で行います。まるっと検索の Web Pixel が marutto_search を受け取ったときにこの値を保存し、その後の「カート追加」「購入完了」に自動で付け直すことで、どの検索が売上につながったかを追跡します。

この値を publish していただかないと、そのストアの検索経由売上は計上されません。 検索は正常に動くため気づきにくい箇所なので、実装時にご確認ください。

8. バージョン運用 ​

  • v1 は少なくとも12か月維持します。廃止する場合は6か月前に告知します
  • v1 として公開したフィールド名・型・意味は変更しません。イベント名も同様です
  • フィールドの追加は v1 のまま行います(既存の実装は影響を受けません)
  • 変更が必要な場合は v2 として新規に提供し、v1 は上記の期間そのまま維持します

実装を委託される場合、納品後にコードに触れる人がいなくなることがあります。12か月という期間は、そのことを前提にしています。Shopify の API バージョンも同じく12か月なので、そちらの更新サイクルに合わせていただけます。

9. 利用できる範囲 ​

検索API は検索結果ページ専用ではありません。App Proxy 経由の同一オリジン通信のため、ストアフロントのどのページからでも利用できます。

  • 検索結果ページ
  • コレクションページの絞り込み
  • 商品ページの関連商品
  • 特集ページ・LP への「このキーワードの商品一覧」の埋め込み
  • 商品ページのレビュー(type=reviews)

チェックアウトは対象外です。Shopify のチェックアウトは拡張機能がサンドボックス内で動作し、通信先も制限されているため、App Proxy を同一オリジンとして呼べません。サンキューページ・注文状況ページも同じ扱いです。

また、ストア外(独自ドメインの Hydrogen、ネイティブアプリ等)からの利用も App Proxy の対象外です。現在は提供していません。ご要望があればお知らせください。

10. レビューの一覧(type=reviews) ​

商品の公開済みのレビューを、新しい順に返します。

GET /apps/marutto-search/search?type=reviews&contract=v1&product_id=gid://shopify/Product/8123456789&limit=10
  • 対象は、まるっと検索に集まったレビューだけです。ほかのレビューアプリの評価の商品(rating.source が external)は空です
  • contract=v1 と同じく Pro プラン以上です。Pro プラン未満では 403 と {"code": "plan_required"} を返します
  • 並びは新しい順(投稿された日の新しい順)だけです。評価で並べ替える・★で絞り込む指定はありません。好意的なレビューだけを選んで載せることにならないよう、こうしています
  • レビューは翻訳されません。多言語のストアでも、投稿されたままの言葉で返ります(locale は効きません)
  • summary は公開済みのレビューから数えます。検索の結果の rating は検索の索引から数えるので、レビューを公開・非表示にした直後は、索引に反映されるまで両者が少しずれることがあります
  • 計測のイベントは不要です(7章は検索だけ)
  • Cache-Control: private, no-store で返します。ストアが非表示にしたレビューや、削除の依頼で消したレビューが残らないよう、保存せずにその都度取得してください

クエリパラメータ ​

パラメータデフォルト内容
contract—v1。必須
type—reviews。必須
product_id—商品の GID(gid://shopify/Product/…)か数字の ID。必須
limit10取得件数。1〜20(それ以外は 10)
cursor—次のページ。前の応答の next_cursor をそのまま渡します
sortnewestnewest のみ。ほかの値は 400

レスポンス ​

json
{
  "schemaVersion": "v1",
  "product_id": "gid://shopify/Product/8123456789",
  "summary": {
    "average": 4.6,
    "count": 23,
    "distribution": { "5": 16, "4": 5, "3": 2, "2": 0, "1": 0 }
  },
  "reviews": [
    {
      "id": "cm2r8x1k0000abcd",
      "rating": 5,
      "title": "形がきれい",
      "body": "洗っても縮まず、春から秋まで着ています。",
      "author_name": "みどり",
      "verification": "shopify_order",
      "incentivized": false,
      "helpful_count": 4,
      "reply": {
        "body": "ご愛用ありがとうございます。",
        "replied_at": "2026-05-02T01:00:00.000Z"
      },
      "images": [
        {
          "url": "https://cdn.shopify.com/s/files/1/0001/0002/files/review.jpg"
        }
      ],
      "published_at": "2026-04-30T03:12:00.000Z",
      "created_at": "2026-04-29T22:40:00.000Z"
    }
  ],
  "next_cursor": "eyJzb3J0IjoibmV3ZXN0Ii…"
}
フィールド型内容
product_idstring商品の GID
summary.averagenumber | null公開済みレビューの平均(小数2桁)。レビューが無い商品は null
summary.countnumber公開済みレビューの件数
summary.distributionobject星ごとの件数("5"〜"1")
reviews[].idstringレビューの ID
reviews[].ratingnumber星の数(1〜5)
reviews[].titlestring | nullタイトル。無い場合は null
reviews[].bodystring本文(プレーンテキスト。改行を含みます)
reviews[].author_namestring投稿者が入力した名前(下記)
reviews[].verificationstring購入の確認(下記)
reviews[].incentivizedboolean特典(クーポンなど)を受け取って書かれたか
reviews[].helpful_countnumber「参考になった」の数
reviews[].replyobject | nullストアからの返信 { body, replied_at }。無い場合は null
reviews[].images[]array写真 { url }(最大5枚)。無い場合は []
reviews[].published_atstring | null公開した日時(ISO 8601)
reviews[].created_atstring投稿された日時(ISO 8601)。並びの基準
next_cursorstring | null次のページがあれば、cursor に渡す値。最後のページは null

verification は次の3つです。2つの「購入確認済み」は意味が違うので、区別して表示してください(まるっと検索のレビュー欄の表示と同じ)。

値意味レビュー欄の表示
shopify_orderこのストアの注文で、購入が確かめられた購入確認済み
merchant_import以前のレビューサービスで購入が確かめられたものを、取り込んだ購入確認済み(インポート)
none購入は確かめていない(表示なし)

表示するときのお願い ​

  • タイトル・本文・投稿者名・返信は、買い物客やストアが書いた文字列です。HTML として挿入せず、エスケープして表示してください(textContent を使うなど)。本文の改行は \n のまま届きます
  • incentivized が true のレビューには、特典を受け取って書かれたことを必ず表示してください。 ステルスマーケティングの規制への対応です。まるっと検索のレビュー欄でも、この表示は消せません
  • ★の数で選んで表示しないでください。 新しい順のまま表示してください。好意的なレビューだけを選んで載せると、ステルスマーケティング規制(景品表示法)や米国 FTC の規則で問題になるおそれがあります。まるっと検索のレビュー機能も、この考え方で作っています(レビュー)
  • author_name は投稿者が入力した名前で、取り込んだレビューでは実名のこともあります。まるっと検索のレビュー欄でも公開している情報です。表示するかどうかはストアのご判断でお決めください

エラー ​

リクエストの誤りでは { error, code } を返します。

状況ステータスcode
product_id が無い・形が違う400invalid_product_id
sort が newest 以外400invalid_sort
filter_rating を付けた400unsupported_filter
cursor が壊れている・別の一覧のもの400invalid_cursor
プランが Pro 未満403plan_required
一時的に読めない503reviews_unavailable / settings_unavailable

まるっと検索