目次
はじめに
eBay の Browse API(/buy/browse/v1/item_summary/search)は、出品検索・商品詳細取得の中心となる API だ。越境 EC で日本向けに eBay 出品を運用するとき、検索パラメータの設計次第で一覧に出てくる商品の質が大きく変わる。
よくある課題はこういったものだ。
- キーワード検索だけだと関連度の低い商品が混ざり、顧客体験が落ちる
- コンディション・配送地域を絞り込まないと、日本に届かない出品や中古品が混入する
- カテゴリ横断でヒットしすぎて、本当に比較すべき商品群が見えにくい
これらをまとめて対処するのが filter / aspect_filter / sort / fieldgroups の組み合わせ設計だ。
検索精度を決める 3 要素
Browse API の検索精度は次の 3 層で制御する。
q×category_ids— キーワードとカテゴリ ID の併用で母集団を絞る。カテゴリを指定しないと全カテゴリが対象になり、ノイズが増えるfilter— 価格帯・コンディション・配送先を制御して、明らかに対象外のものを除くaspect_filter— カテゴリ固有の属性(ブランド・サイズ・素材など)で商品属性を絞る
3 層を順に適用し、母集団を小さくしてから sort で並べ替えるのが基本の流れだ。
実装パターン 1: 基本リクエストの組み立て
まず q と category_ids だけのシンプルな検索から始める。
const BASE_URL = "https://api.ebay.com/buy/browse/v1/item_summary/search"
async function searchItems(params: URLSearchParams, token: string) {
const res = await fetch(`${BASE_URL}?${params}`, {
headers: {
Authorization: `Bearer ${token}`,
"X-EBAY-C-MARKETPLACE-ID": "EBAY_US", // 対象マーケットプレイス
"X-EBAY-C-ENDUSERCTX": "contextualLocation=country=JP", // エンドユーザーのロケーション
},
})
if (!res.ok) throw new Error(`Browse API error: ${res.status}`)
return res.json()
}
const params = new URLSearchParams({
q: "vintage denim jacket",
category_ids: "3002", // Men's Clothing
limit: "50",
offset: "0",
})
const result = await searchItems(params, accessToken)X-EBAY-C-ENDUSERCTX ヘッダに country=JP を渡すと、配送可否の文脈が日本基準になる。省略するとデフォルト(EBAY_US の場合は米国)が使われるため、日本向け開発では必ず指定したい。
実装パターン 2: filter 構文で除外条件を積む
filter は複数条件をカンマ区切りで並べ、AND 条件として機能する。
// price:[10..50] — 価格帯(ドル)
// conditions:{NEW|LIKE_NEW} — コンディション
// deliveryCountry:JP — 日本への配送あり
// itemLocationCountry:JP — 出品者が日本にいる(越境 EC では外す場合も)
const params = new URLSearchParams({
q: "vintage denim jacket",
category_ids: "3002",
filter: [
"price:[10..50]",
"conditions:{NEW|LIKE_NEW}",
"deliveryCountry:JP",
].join(","),
priceCurrency: "USD",
limit: "50",
offset: "0",
})条件の構文はフィールドごとに異なる。主なパターンを整理しておく。
| filter 条件 | 書き方の例 | 備考 |
|---|---|---|
| 価格帯 | price:[10..50] | priceCurrency と併用 |
| 上限なし | price:[10..] | ピリオド 2 つで範囲の片方を省略 |
| コンディション | conditions:{NEW} または {NEW|LIKE_NEW} | | で OR |
| 配送先 | deliveryCountry:JP | ISO 3166-1 alpha-2 |
| 出品者所在国 | itemLocationCountry:JP | 日本出品者に絞る場合 |
| 送料無料 | maxDeliveryCost:0 | 0 を指定で無料のみ |
実装パターン 3: aspect_filter でカテゴリ属性を絞る
aspect_filter はカテゴリ固有の属性(アスペクト)で絞り込む。どのアスペクト名・値が使えるかは category_id によって異なるため、事前に /buy/browse/v1/item_aspect_for_category エンドポイントで取得しておく必要がある。
// aspect_filter の書き方: aspectName:{value1|value2}
// 複数のアスペクトはカンマ区切り(AND)
const params = new URLSearchParams({
q: "denim jacket",
category_ids: "3002",
filter: "conditions:{NEW|LIKE_NEW},deliveryCountry:JP",
aspect_filter: [
"Brand:{Levi's|Wrangler}",
"Size:{M|L|XL}",
"Color:{Blue|Dark Blue}",
].join(","),
limit: "50",
offset: "0",
})aspect_filter の値は 大文字・小文字・スペースを含むカテゴリ定義のままの文字列でなければヒットしない。Levi's を levis と書くと 0 件になる。アスペクト値の一覧も /item_aspect_for_category から取得して、そのまま使うのが安全だ。
実装パターン 4: sort と fieldgroups の最適化
sort は母集団を絞った後で効かせる。デフォルトは sort=BEST_MATCH(関連度順)で、価格・新着・距離などに切り替えられる。
// sort の主な選択肢
// BEST_MATCH (デフォルト) — eBay アルゴリズムによる関連度
// PRICE — 価格昇順
// -PRICE — 価格降順
// NEWLY_LISTED — 新着順
// ENDING_SOONEST — 終了間近 (オークション形式)
const params = new URLSearchParams({
q: "denim jacket",
category_ids: "3002",
filter: "conditions:{NEW},deliveryCountry:JP",
sort: "PRICE",
fieldgroups: "MATCHING_ITEMS,ASPECT_REFINEMENTS",
limit: "50",
offset: "0",
})fieldgroups はレスポンスに含めるフィールドグループを指定する。デフォルトの MATCHING_ITEMS に加えて ASPECT_REFINEMENTS を追加すると、絞り込み候補のアスペクト一覧をフロント側に返せる。ファセット検索 UI を作る場合に使う。不要なフィールドグループを外せばレスポンスサイズが小さくなり、レイテンシも改善する。
実装パターン 5: ページネーションと上限管理
Browse API の search エンドポイントは offset + limit 方式でページネーションを行う。
async function* paginateSearch(
baseParams: URLSearchParams,
token: string,
pageSize = 50,
) {
const ABSOLUTE_LIMIT = 10_000 // offset + limit の合計上限
let offset = 0
while (offset < ABSOLUTE_LIMIT) {
const params = new URLSearchParams(baseParams)
params.set("limit", String(pageSize))
params.set("offset", String(offset))
const data = await searchItems(params, token)
const items: unknown[] = data.itemSummaries ?? []
if (items.length === 0) break
yield items
offset += items.length
// total が取得済み件数以下なら終了
if (data.total <= offset) break
}
}
// 使い方
for await (const page of paginateSearch(baseParams, accessToken)) {
for (const item of page) {
// 商品を処理
}
}注意点は 2 つある。まず limit の最大値は 200(/item_summary/search の場合)。201 以上を指定するとエラーになる。もう一つは offset + limit の合計が 10,000 を超えるリクエストは拒否される仕様だ。10,000 件を超える結果セットが必要なときは、filter や category_ids でさらに母集団を分割して並列検索するアプローチが現実的だ。
実装パターン 6: 検索ログからの継続改善
実運用では、検索パラメータを固定するのではなく ログを分析して継続的に改善するサイクルを回すのが効果的だ。
type SearchLog = {
query: string
filter: string
aspectFilter: string
total: number
firstPageItemCount: number
searchedAt: Date
}
async function searchWithLogging(
params: URLSearchParams,
token: string,
): Promise<{ data: unknown; log: SearchLog }> {
const start = Date.now()
const data = await searchItems(params, token)
const log: SearchLog = {
query: params.get("q") ?? "",
filter: params.get("filter") ?? "",
aspectFilter: params.get("aspect_filter") ?? "",
total: data.total ?? 0,
firstPageItemCount: (data.itemSummaries ?? []).length,
searchedAt: new Date(start),
}
// total が 0 のクエリや firstPageItemCount が極端に少ない場合はアラート対象
if (log.total === 0 || log.firstPageItemCount < 5) {
console.warn("low-yield search", log)
}
return { data, log }
}total === 0 のクエリは aspect_filter の値ミスマッチか、filter の組み合わせが厳しすぎることが多い。ログを集めて、total の分布とよく使われるアスペクト値を定期的に確認する習慣をつけておくと、パラメータ設計を段階的に改善できる。
まとめ
| 課題 | 対策 |
|---|---|
| 関連度の低い商品が混入する | q × category_ids で母集団を絞る |
| 日本に届かない出品が出る | filter=deliveryCountry:JP を必ず付ける |
| コンディション混在 | filter=conditions:{NEW|LIKE_NEW} で制限 |
| カテゴリ属性の絞り込みが効かない | aspect_filter の値はカテゴリ定義のまま使う |
| レスポンスが重い | fieldgroups で不要グループを外す |
| 10,000 件超の全件取得が詰まる | filter で母集団を分割して並列検索 |
| パラメータを改善する根拠がない | total / firstPageItemCount を記録してログ分析 |



