getItem は、特定の商品ID(ItemID)を使って、eBay上の商品詳細データを取得するAPI です。タイトル、価格、画像URL、配送情報、カテゴリ、状態、ItemSpecifics(商品属性)など、1点の商品に関する多くの情報をまとめて取ることができます。
この記事では、eBay Browse API の getItem エンドポイントを、Google Apps Script(GAS)から呼び出す方法を紹介します。
このサンプルコードでは、実行前にアクセストークンの取得(有効性の確認)を行い、その後 getItem を呼び出してデータを取得するという流れになっています。
- ✅ 数値の ItemID を渡すだけで実行可能
- ✅ 結果を 元コード互換の配列([0〜15])で返却
- ✅
ItemSpecifics(商品属性)は[name, value]形式の配列で取得 - ✅
calcStatus()で終了日時を元に簡易販売状態を付与
1. 使用する API
◆ Browse API / getItem
- 商品 ID(ItemID)をキーに 商品詳細を取得します。
- API Docs: getItem Browse API
2. 事前準備
- eBay Developer Program に登録
- App を作成して Client ID / Client Secret を取得
- GAS のプロジェクト設定 → 「スクリプトのプロパティ」に各パラメータを保存
※Google Apps Script → 歯車 → プロジェクトのプロパティ → スクリプトのプロパティ
| プロパティ名 | 値 / 説明 |
|---|---|
CLIENT_ID1 | Client ID |
CLIENT_SECRET1 | Client Secret |
MARKETPLACE_ID | 任意(既定: EBAY_US) |
※MARKETPLACE_ID:EBAY_US / EBAY_GB / EBAY_DE / EBAY_AU …
3. サンプルコード(最終版)
下記コードを GAS にそのまま貼り付ければ実行できます。
サンプルコード(クリックして開く)
/***********************
* eBay Browse API: getItem(元の出力形式に準拠・最終版)
* - 数値の ItemID を渡すと v1 形式に自動変換(例: "177..." → "v1|177...|0")
* - client_credentials(アプリ認証)でアクセストークン取得&CacheServiceで短期キャッシュ
* - 401(期限切れ)時はトークンを取り直して1回だけ再試行
* - MARKETPLACE(例: EBAY_US)をヘッダで指定
* - 出力は配列 [0..15] で、あなたの元コードと同じ順番
***********************/
const CLIENT_ID = PropertiesService.getScriptProperties().getProperty('CLIENT_ID1');
const CLIENT_SEC = PropertiesService.getScriptProperties().getProperty('CLIENT_SECRET1');
const MARKETPLACE_ID =
PropertiesService.getScriptProperties().getProperty('MARKETPLACE_ID') || 'EBAY_US';
const EBAY_OAUTH_TOKEN_URL = 'https://api.ebay.com/identity/v1/oauth2/token';
const EBAY_BROWSE_BASE = 'https://api.ebay.com/buy/browse/v1/item/';
/** 動作テスト(開発時用) */
function demo_getItem() {
const legacyItemId = "177533256340"; // ← テストしたい ItemID に変更
const data = ebayBrowseGetItem(legacyItemId);
//Logger.log(data); // 返却配列([0..15])を確認したい場合に有効化
}
/**
* getItem 呼び出しの入口
* @param {string} itemId - 数値ID または "v1|<id>|0"
* @return {Array|null} - あなたの元配列形式で返す
*/
function ebayBrowseGetItem(itemId) {
if (!CLIENT_ID || !CLIENT_SEC) {
throw new Error("CLIENT_ID1 / CLIENT_SECRET1 が未設定です(スクリプトのプロパティに保存してください)");
}
// 数値IDで渡された場合、Browse API 推奨の "v1|<id>|0" 形式へ変換
const browseId = itemId.startsWith("v1|") ? itemId : `v1|${itemId}|0`;
// ① トークン(キャッシュ優先)
let token = getAppTokenCached();
// ② API 実行
let res = callGetItem(browseId, token);
// 401(期限切れ等)の場合は 1 回だけトークン再取得 → 再試行
if (res._httpCode === 401) {
token = refreshAppToken();
res = callGetItem(browseId, token);
}
// 200 以外は失敗としてログを残して終了
if (res._httpCode !== 200 || !res._json) {
Logger.log(`❌ getItem Error: ${res._httpCode}`);
Logger.log(res._rawText);
return null;
}
// 元コード互換の整形&ログ出力
return displayItemData(res._json);
}
/**
* 元の displayItemData() と互換の配列を返しつつ、
* ブログに載せやすいログを出力する処理。
*/
function displayItemData(data) {
// eBay 側エラー
if ("errors" in data) {
Logger.log("❌ APIエラー: " + JSON.stringify(data.errors));
return null;
}
// --- 基本情報の抽出 ---
const title = data.title || "";
const image = data.image?.imageUrl || "";
const price = data.price?.value || "";
const currency = data.price?.currency || "";
// 送料(最初のオプションを採用)
const shippingOpt = data.shippingOptions?.[0] || {};
const shipping = shippingOpt.shippingCost?.value || "";
const shippingCurrency = shippingOpt.shippingCost?.currency || "";
// ★ 在庫・販売ステータス(復活版):終了日時で簡易判定
const status = calcStatus(data.itemEndDate); // "instock" / "outofstock"
// セラー情報
const seller = data.seller?.username || "";
const feedbackPercent = data.seller?.feedbackPercentage || "";
const feedbackScore = data.seller?.feedbackScore || "";
// カテゴリ・コンディション
const categoryId = data.categoryId || "";
const categoryName = data.categoryPath || "";
const condition = data.condition || "";
const conditionId = data.conditionId || "";
// URL・所在地
const itemUrl = data.itemWebUrl || "";
const loc = data.itemLocation;
const location = loc ? `${loc.postalCode || ""} ${loc.country || ""}`.trim() : "";
// 説明文(HTML タグを排除してプレーン化)
let description = (data.description || "")
.replace(/<br\s*\/?>/gi, "\n")
.replace(/<\/p>/gi, "\n")
.replace(/<[^>]*>/g, "")
.trim();
// ItemSpecifics(localizedAspects)→ [name, value] の配列へ整形
let itemSpecifics = [];
if (Array.isArray(data.localizedAspects)) {
itemSpecifics = data.localizedAspects.map(sp => {
const name = sp.name || "";
const value = Array.isArray(sp.value) ? sp.value.join(", ") : (sp.value || "");
return [name, value];
});
}
// 日付系(JST 文字列)
const ListDate = toJST(data.itemCreationDate) || "";
const EndDate = toJST(data.itemEndDate) || "";
// --- ログ出力 ---
Logger.log(`Title: ${title}`);
Logger.log(`Image URL: ${image}`);
Logger.log(`Price: ${price} ${currency}`);
Logger.log(`Shipping: ${shipping} ${shippingCurrency}`);
Logger.log(`Status: ${status}`);
Logger.log(`Seller ID: ${seller}`);
Logger.log(`Feedback: ${feedbackPercent}% (${feedbackScore})`);
Logger.log(`Category: ${categoryName} (ID: ${categoryId})`);
Logger.log(`Ccondition: ${condition} (ID: ${conditionId})`);
Logger.log(`Item URL: ${itemUrl}`);
Logger.log(`Location: ${location}`);
//Logger.log(`Description:\n${description}`); // ← 長くなる場合は必要に応じて有効化
Logger.log(`ListDate: ${ListDate}`);
Logger.log(`EndDate : ${EndDate}`);
Logger.log(`ItemSpecifics : ${itemSpecifics}`);
// --- 呼び出し元と同じ配列形式で返却([0..15]) ---
return [
title, // 0
price, // 1
shipping, // 2
status, // 3 ← calcStatus の結果
seller, // 4
feedbackScore,// 5
categoryId, // 6
categoryName, // 7
condition, // 8
conditionId, // 9
itemUrl, //10
image, //11
description, //12
ListDate, //13
EndDate, //14
itemSpecifics //15 例: [["Brand","Canon"],["Model","EOS M6"], ...]
];
}
/********************************************
* ↓ 以降:HTTP呼び出し・トークン管理・ユーティリティ
********************************************/
/** Browse getItem を実行(HTTP) */
function callGetItem(browseItemId, token) {
const url = EBAY_BROWSE_BASE + encodeURIComponent(browseItemId);
// マーケット指定で価格/送料の文脈が変わります(例:EBAY_US)
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"X-EBAY-C-MARKETPLACE-ID": MARKETPLACE_ID,
"Accept-Language": "en-US", // 表示言語のヒント(任意)
// "X-EBAY-C-ENDUSERCTX": "contextualLocation=country=JP", // 配送見積もりの文脈を与えたい場合など
};
const opt = { method: "get", headers, muteHttpExceptions: true };
const resp = UrlFetchApp.fetch(url, opt);
const code = resp.getResponseCode();
return {
_httpCode: code,
_rawText: resp.getContentText(),
_json: safeJson(resp.getContentText())
};
}
/** 在庫・販売ステータスの簡易判定
* - itemEndDate が現在時刻より過去 → "outofstock"
* - それ以外 → "instock"
* - 注意:実在庫と完全一致ではなく「終了日時ベースの目安」
*/
function calcStatus(itemEndDate) {
if (!itemEndDate) return "instock";
const end = new Date(itemEndDate);
const now = new Date();
return end <= now ? "outofstock" : "instock";
}
/** アプリトークン(client_credentials)をキャッシュ優先で取得 */
function getAppTokenCached() {
const cache = CacheService.getScriptCache();
const c = cache.get("EBAY_APP_TOKEN");
if (c) return c;
const t = refreshAppToken();
if (t) cache.put("EBAY_APP_TOKEN", t, 60 * 25); // 25分キャッシュ(有効30分想定)
return t;
}
/** 強制的に新しいトークンを発行(401時など) */
function refreshAppToken() {
const basic = Utilities.base64Encode(`${CLIENT_ID}:${CLIENT_SEC}`);
const payload =
"grant_type=client_credentials&scope=" +
encodeURIComponent("https://api.ebay.com/oauth/api_scope"); // Browseの読み取りはこれで十分
const resp = UrlFetchApp.fetch(EBAY_OAUTH_TOKEN_URL, {
method: "post",
headers: {
Authorization: `Basic ${basic}`,
"Content-Type": "application/x-www-form-urlencoded",
},
payload: payload,
muteHttpExceptions: true,
});
if (resp.getResponseCode() !== 200) {
Logger.log("Token error");
Logger.log(resp.getContentText());
return null;
}
return JSON.parse(resp.getContentText()).access_token;
}
/** ISO8601 → JST "yyyy/mm/dd" 文字列に整形(表示用の簡易版) */
function toJST(iso) {
if (!iso) return "";
const d = new Date(iso); // UTC ベース
const j = new Date(d.getTime() + 9 * 3600 * 1000); // JST = UTC+9
const y = j.getFullYear();
const m = String(j.getMonth() + 1).padStart(2, "0");
const dd = String(j.getDate()).padStart(2, "0");
return `${y}/${m}/${dd}`;
}
/** JSON を安全にパース(失敗時 null) */
function safeJson(txt) {
try { return JSON.parse(txt); } catch (e) { return null; }
}
4. コード概要
◆ demo_getItem()
- 動作確認用のテスト関数
- 数字の ItemID を渡せば OK
function demo_getItem() {
const legacyItemId = "177533250000";
const data = ebayBrowseGetItem(legacyItemId);
}
◆ ebayBrowseGetItem(itemId)
- getItem の実行入口
- 数値 ItemID を
v1|<id>|0に自動変換 - トークン取得 → API → JSON → 整形 → 配列返却
◆ トークン管理
- 実行前にアクセストークンを取得し、期限内であれば再利用します
- 401(期限切れなど)の場合は、アクセストークンを再取得して再試行します
※技術メモ(必要な人向け) eBay API が用意する「client_credentials」という方式でアクセストークンを取得しています。 (ユーザーログイン不要/アプリ単体で取得可能)
◆ displayItemData(data)
- レスポンス JSON を整形し、元コード互換の配列
[0..15]で返却 - 同時にログを出力し、ブログ記載用にも使える
取り出し項目(抜粋)
| index | 内容 |
|---|---|
| 0 | title |
| 1 | price |
| 2 | shipping |
| 3 | status |
| 4 | seller |
| 5 | feedbackScore |
| 6 | categoryId |
| 7 | categoryName |
| 8 | condition |
| 9 | conditionId |
| 10 | itemUrl |
| 11 | image |
| 12 | description |
| 13 | ListDate |
| 14 | EndDate |
| 15 | itemSpecifics |
5. 販売状態 status 判定
itemEndDate を使って、簡易的な販売状態を付与します。
※eBay の実在庫・出品状態と必ずしも一致するわけではありませんが、傾向を掴む用途には有効です。
| 状態 | 条件 |
|---|---|
instock | 終了日時 > 現在 |
outofstock | 終了日時 ≤ 現在 |
6. ItemSpecifics の扱い
localizedAspects を
[ [ name, value ], ... ]
形式に整形して返しています。
例)Brand を取得
const specs = data[15]; // itemSpecifics
for (const [name, value] of specs) {
if (name === "Brand") Logger.log(value);
}
8. まとめ
eBay の商品データは、Google Apps Script(GAS)から手軽に取得できます。ItemID を渡すだけで、タイトルや価格、配送情報、カテゴリ、状態、ItemSpecifics など幅広い情報を取得でき、既存コードとの互換性を保った配列形式で扱えるため、後続の処理にも流用しやすい点が大きな魅力です。
また、トークンは自動で取得・キャッシュされ、有効期限切れの際も自動再試行が行われるため、日次や定期的なバッチ処理にも安心して組み込めます。取得したデータをスプレッドシートへ連携し、価格調整や在庫チェックなどの用途に応用すれば、業務自動化の可能性がさらに広がります。
ぜひ、GAS と eBay API を組み合わせて、日々の運用を柔軟に効率化してみてください。
