0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

楽天APIが2026年に作り替えられていた — 移行後に踏んだ3つの罠と、動くまでの手順

0
Posted at

楽天ウェブサービスのAPIで商品価格を取得しようとしたところ、古い情報どおりに実装しても一切動きませんでした。

原因は、2026年2月のリニューアルと5月14日の旧方式停止です。移行そのものの告知記事は見つかるのですが、移行後に実際に踏む落とし穴がまとまっていなかったので、動くまでに詰まった3点を残します。

Node.js の標準機能だけで動く実装も最後に置きます。

罠1: ドメインもパスも変わっている

まずここで止まります。

旧(2026-05-14に停止)
ドメイン app.rakuten.co.jp openapi.rakuten.co.jp
パス /services/api/... /ichibams/api/...

商品検索の新しいエンドポイントはこうなります。

https://openapi.rakuten.co.jp/ichibams/api/IchibaItem/Search/20220601

ドメインだけ直してもパスが古いままだと通りません。両方変える必要があります。

罠2: applicationId だけでは通らない。accessKey が必須

旧方式は applicationId だけで呼べましたが、新方式は2点セットです。

?applicationId=<UUID形式>&accessKey=<pk_で始まる文字列>

applicationId も形式が変わり、数字の羅列からUUID形式になりました。旧IDは使えないので、アプリの再登録が必要です。

片方だけだとこうなります。

// accessKey を付けなかった場合
{ "errors": { "errorCode": 400,
  "errorMessage": "accessKey must be present as a query parameter or in the header" } }

// accessKey が正しくない場合
{ "errors": { "errorCode": 403, "errorMessage": "Invalid Access Key" } }

この2つは切り分けに使えます。 400 が返るなら渡し方の問題、403 なら値そのものの問題です。私はこれで「実装は合っていて、キーの値だけが違う」と特定できました。

accessKey は画面上で省略表示されることがある

管理画面からコピーした accessKey13文字しかなく、403 が消えませんでした。正しい値は 46文字です。

pk_ で始まる文字列が短い場合は、省略表示された一部だけをコピーしている可能性があります。コピーボタンがあればそれを使ってください。

罠3(最重要): アプリの種類で認証方式が違う

これが一番厄介でした。新方式のアプリには2種類あり、認証の仕組みそのものが異なります。

種類 認証方法 用途
Webアプリケーション型 登録したサイトURL(Originヘッダ) ブラウザから呼ぶ
バックエンドサービス型 登録したIPアドレス(最大9個) サーバー/PCのプログラムから呼ぶ

サーバーサイドのプログラムから呼ぶのに Webアプリケーション型で登録すると、キーが正しくても認証が通りません(503 Authentication service error になる例が報告されています)。

さらに、それぞれでやってはいけないことがあります。

  • バックエンドサービス型に Origin ヘッダを付けてはいけない(付けると503)
  • 回線の外部IPが変わると403になる → 楽天のアプリ設定で許可IPを更新する

私は最初 Webアプリケーション型で登録してしまい、種類を変えて解決しました。cronやバッチから叩くならバックエンドサービス型です。

外部IPが変わる問題への備え

IP認証である以上、回線側の都合でIPが変わると突然全滅します。気づけないのが一番まずいので、全件失敗したときに現在の外部IPを出すようにしました。

if (ok === 0 && ng > 0) {
  const ip = (await (await fetch("https://api.ipify.org")).text()).trim();
  console.error(`★全件失敗。現在の外部IP: ${ip}`);
  console.error("楽天のアプリ設定「許可されたIPアドレス」がこれと違えば原因はそれ。");
}

エラーメッセージに次にやることまで書いておくと、数週間後の自分が助かります。

動く実装(Node.js 標準のみ・依存ゼロ)

const ENDPOINT =
  "https://openapi.rakuten.co.jp/ichibams/api/IchibaItem/Search/20220601";

async function search({ appId, accessKey, affiliateId }, params) {
  const url = new URL(ENDPOINT);
  const q = {
    applicationId: appId,
    accessKey,                       // ← 新方式では必須
    ...(affiliateId ? { affiliateId } : {}),
    format: "json",
    formatVersion: "2",              // 2 にすると Items が素直な配列になる
    ...params,
  };
  for (const [k, v] of Object.entries(q)) url.searchParams.set(k, v);

  // バックエンドサービス型では Origin を付けない
  const res = await fetch(url, { headers: { "User-Agent": "my-app/1.0" } });
  if (!res.ok) throw new Error(`HTTP ${res.status} ${await res.text()}`);
  return res.json();
}

const json = await search(cfg, { keyword: "コーヒー豆 1kg", hits: 30, sort: "standard" });
for (const it of json.Items) console.log(it.itemPrice, it.itemName);

formatVersion: "2" は地味に効きます。指定しないと Items[{ Item: {...} }] という一段深い形で返ってくるためです。

おまけ: 「最安値」を素直に取ると、たいてい間違える

APIが通ったあとに気づいたのですが、取得した最安値がそのままでは使い物になりませんでした。

  • 「オリーブオイル 1000ml」の最安値がボディソープだった
  • 「ホエイプロテイン 3kg」の最安1,280円は、同じページ内の500g品の価格
  • 「SSD 1TB」の最安は256GBの価格

理由は2つです。

  1. キーワードが偶然当たっただけの別商品が混ざる
  2. 楽天は複数容量を1ページにまとめた商品が多く、検索結果にはその中で最も安いバリエーションの価格が出る

対策として、商品名による除外を入れました。

const keep = (name) => {
  const n = name.toLowerCase();
  if (ng.some((s) => n.includes(s))) return false;   // 例: ボディソープ, シャンプー
  return must.every((s) => n.includes(s));           // 例: 1kg
};

もう1つ効いたのが sort"+itemPrice" から "standard" に変えたことです。

安い順で30件取ると、上位が別容量の安い品で埋まり、本来見たい商品が1件も入らないカテゴリがありました(コーヒー豆で0件、SSDで1件)。「安い順に取れば最安が分かる」は成り立ちません。

なお、この方法でも**容量が商品名に書かれていないまとめ売りページは排除できません。**なので数字を出すときは「検索結果のうち条件に合うものの最安」と明記するのが正確です。

まとめ

  • ドメインとパスの両方を新しいものに変える
  • applicationIdaccessKey2点セット。400 と 403 で切り分けられる
  • accessKey が短いときは省略表示を疑う(正しくは46文字)
  • プログラムから叩くならバックエンドサービス型。Origin は付けない。IP変更の検知を仕込む
  • 取得した最安値はそのままでは信用しない

移行そのものより、移行後に「動いているのに間違っている」状態に気づけるかのほうが手間でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?