TECH JOURNAL

楽天 RMS 商品 API 一括更新の落とし穴 — 文字化け・文字数制限・SKU 制約を攻略する

ShareXB!
楽天 RMS 商品 API 一括更新の落とし穴 — 文字化け・文字数制限・SKU 制約を攻略する
目次

はじめに

楽天市場で大量の商品を管理していると、商品情報の一括更新を API で自動化したくなる。しかし RMS の商品 API はいくつかの「罠」があり、ナイーブに実装するとすぐにデータ破損が起きる。

実際の受託案件で踏んだハマりどころを、対処コード付きで解説する。

ハマりどころ 1: 文字コードの混在

RMS Web API の商品系 API は、フィールドによって UTF-8 と Shift_JIS が混在していることがある(特に旧 API との後方互換性のために残っているフィールド)。JSON API は基本 UTF-8 だが、CSV 出力や旧エンドポイントを使う場合は注意が必要。

対処として、入稿前にすべての文字列を Shift_JIS で表現可能かチェックする関数を挟む。

// Shift_JIS に変換できない文字を検出する
import iconv from "iconv-lite"
 
function containsUnsupportedChars(text: string): boolean {
  const encoded = iconv.encode(text, "Shift_JIS")
  const decoded = iconv.decode(encoded, "Shift_JIS")
  return decoded !== text
}
 
// 入稿前のサニタイズ
function sanitizeForRms(text: string): string {
  // ① 絵文字・特殊 Unicode を除去
  const noEmoji = text.replace(/[\u{1F000}-\u{1FFFF}]/gu, "")
  // ② 全角←→半角の統一(必要に応じて)
  return noEmoji
}

商品名に顧客が入力した絵文字が混入してくるケースが多い。入力時点で弾くか、API 入稿前にサニタイズするかを設計段階で決めておく。

ハマりどころ 2: 商品名 127 文字制限(バイト数)

楽天の商品名は 127 文字(全角は 1 文字 = 2 バイト換算で 64 文字が上限目安)という制限がある。この「127」がバイト数なのか文字数なのかで混乱しやすい。

実際の制限は 127 バイト(Shift_JIS 換算)。全角文字はほとんど 2 バイトなので、全角 63 文字 + 半角 1 文字がギリギリのライン。

const ELLIPSIS = "…"
const ELLIPSIS_BYTES = iconv.encode(ELLIPSIS, "Shift_JIS").length // Shift_JIS で 2 バイト
const MAX_BYTES = 127
 
function truncateToRmsItemName(name: string): string {
  const encoded = iconv.encode(name, "Shift_JIS")
  if (encoded.length <= MAX_BYTES) return name
 
  // 省略記号分のバイトを先に確保してから切り詰める
  const limit = MAX_BYTES - ELLIPSIS_BYTES
  let byteCount = 0
  let charCount = 0
  for (const char of name) {
    const charBytes = iconv.encode(char, "Shift_JIS").length
    if (byteCount + charBytes > limit) break
    byteCount += charBytes
    charCount++
  }
  return name.slice(0, charCount) + ELLIPSIS
}

自動で末尾に「…」を付けるかどうかはビジネス判断。商品名が短くなると検索ヒット率に影響するので、別途 SEO チームと調整が必要な場合もある。

ハマりどころ 3: SKU 数の上限

楽天の 1 商品(アイテム)に登録できる SKU 数は上限がある。現状の制限は 1 商品あたり最大 10,000 SKU だが、実際には数百 SKU を超えたあたりから管理画面の動作が重くなる。

大量 SKU を持つ商品は、以下のいずれかの方針を取る。

  1. 商品を分割: サイズ別・カラー別に複数の楽天アイテムに分割
  2. SKU を整理: 販売終了 SKU を API で非表示化(inventory の showFlag を 0 に設定)
// 販売終了 SKU を非表示にする
async function hideDiscontinuedSku(itemUrl: string, skuId: string) {
  return callRmsApi("item/2.0/update", {
    itemUrl,
    skuInfo: {
      skuId,
      inventory: {
        showFlag: 0, // 0: 非表示
        orderFlag: 0,
      },
    },
  })
}

ハマりどころ 4: 在庫 0 と非表示の違い

在庫を 0 に設定すると「在庫なし」として表示され続ける(楽天の在庫なし表示が出る)。「この SKU は取り扱わない」という場合は showFlag: 0 で非表示にする必要がある。

混同すると、取り扱い終了した商品が「在庫なし」として延々と表示され続け、顧客体験が悪化する。

一括更新時のバッチサイズ

item/2.0/update は 1 リクエストで 1 商品しか更新できない。大量商品を更新する場合は適切なバッチ間隔が必要。

async function bulkUpdateItems(items: RmsItem[]) {
  const BATCH_SIZE = 10
  const DELAY_MS = 200 // 200ms インターバルでスロットリング
 
  for (let i = 0; i < items.length; i += BATCH_SIZE) {
    const batch = items.slice(i, i + BATCH_SIZE)
    await Promise.all(batch.map((item) => updateItem(item)))
 
    if (i + BATCH_SIZE < items.length) {
      await new Promise((resolve) => setTimeout(resolve, DELAY_MS))
    }
  }
}

まとめ

問題対策
文字化けShift_JIS エンコード可能か事前チェック
商品名が長すぎる127 バイト(Shift_JIS)でバイト数ベースで切り詰め
SKU 数肥大化販売終了 SKU を showFlag: 0 で非表示化
在庫 0 と非表示の混同取り扱い終了は showFlag: 0、一時欠品は在庫 0
レートリミットバッチサイズ 10 件 + 200ms インターバル
ShareXB!

この記事を書いた人

渡部 誠也

執行役員 / CTO

独立系 SIer で Web・組み込み・基幹システムの開発を経験し、2017 年に illustrious へ。CTO としてシステム開発事業を立ち上げ、要件定義からコーディングまで一貫して担う。EC に特化した Web アプリケーションを数多く手がける。

ECの業務やシステムについて、
ご相談ください。

いまの運用で困っていること、実現したいことから、一緒に整理します。