「Yahoo!ショッピング商品検索API(ItemSearch)を使って価格差リサーチツールを作ったら、途中から429(Too Many Requests)が連発して止まってしまう」——これはYahoo!ショッピングAPI ItemSearchを使う開発者が必ず一度は踏む壁です。原因は明確で、Yahoo!ショッピングAPIには1分あたり30リクエストという利用制限があり、これを超えると即座に429エラーが返ってきます。本記事では、この制限を回避する待機・リトライの実装を、実際にせどり向けリサーチSaaSを開発した経験をもとに解説します。
Yahoo!ショッピングAPI ItemSearchの「1分30リクエスト」制限とは
Yahoo!ショッピングAPI(ShoppingWebService V3 itemSearch)は、アプリケーションID(appid)単位で1分間に30リクエストまでという利用制限が設けられています。楽天市場APIなど他のECモール系APIも同様の制限を持ちますが、Yahoo!ショッピングAPIは特に上限が低く、複数キーワードを連続で検索する用途では真っ先に引っかかるポイントです。
429エラーが出る典型パターン
- キーワードリスト(数百件)を
forループで単純に回してrequests.get()を連打する - ページネーション(
results・pageパラメータ)で1キーワードあたり複数回リクエストしている - 複数スレッド・非同期処理で並列にリクエストを投げている(並列化すると制限にはるかに早く到達する)
制限は「アプリケーションID単位」か「IP単位」か
実装上はappid単位で管理しておくのが安全です。同一IPから複数appidで叩けば回避できると考えるのは規約違反のリスクがあるため避けてください(後述)。制限の正確な仕様・最新の数値はYahoo!デベロッパーネットワークの公式ドキュメントで随時更新されるため、実装前に必ず一次情報を確認する習慣をつけましょう。
待機・リトライの実装設計
単純なtime.sleepでは足りない理由
「2秒待てば1分30回に収まるだろう」とtime.sleep(2)を挟むだけの実装をよく見かけますが、これは危険です。処理自体の実行時間(ネットワーク遅延・JSON解析)がリクエストごとにばらつくため、積み重なると60秒の窓に31回目が滑り込み、結局429が出ます。確実に守るには直近60秒間に何回叩いたかを記録するトークンバケット(スライディングウィンドウ)方式が必要です。
トークンバケット方式で30リクエスト/分を守る
import time
import requests
from collections import deque
class RateLimiter:
def __init__(self, max_calls=28, period=60):
# 30に対して余裕を持たせて28に設定(サーバー側の時間ズレを吸収)
self.max_calls = max_calls
self.period = period
self.calls = deque()
def wait(self):
now = time.monotonic()
while self.calls and now - self.calls[0] > self.period:
self.calls.popleft()
if len(self.calls) >= self.max_calls:
sleep_for = self.period - (now - self.calls[0]) + 0.1
time.sleep(max(sleep_for, 0))
self.calls.append(time.monotonic())
limiter = RateLimiter()
def search_item(session, appid, query, page=1, retries=3):
limiter.wait()
params = {"appid": appid, "query": query, "results": 30, "page": page}
resp = session.get(
"https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch",
params=params,
timeout=10,
)
if resp.status_code == 429:
if retries
ポイントは2つです。1つ目は上限を30ではなく28程度に余裕を持たせること。サーバー側とクライアント側の時刻ズレやネットワーク往復時間の分、実際の窓は多少ブレます。2つ目は429が返ってきた場合の指数バックオフ・再試行回数の上限を必ず設けること。無限リトライにすると、appidそのものに何らかの異常(キー無効化・規約違反フラグ)が起きた場合にプロセスが無限ループして張り付きます。
大量キーワード取得時の設計
数百キーワードを回すときの現実的な所要時間
1分30リクエストという制限は、裏を返せば1リクエストあたり平均2秒のペースが上限ということです。1キーワードにつき複数ページ取得する設計であれば、数百キーワードの全件取得には数十分〜数時間かかる計算になります。この所要時間を事前に見積もっておかないと、「バッチが夜間に終わらず翌朝の業務に間に合わない」といった事故につながります。
失敗時の再開設計(べき等性)
長時間バッチでは、途中でネットワーク断・PCスリープ・API側の一時障害が必ず起きます。処理済みキーワードをログに残し、再実行時にスキップする設計にしておくと、失敗しても最初からやり直さずに済みます。
import json
from pathlib import Path
DONE_LOG = Path("done_keywords.json")
def load_done():
return set(json.loads(DONE_LOG.read_text())) if DONE_LOG.exists() else set()
def save_done(done):
DONE_LOG.write_text(json.dumps(sorted(done), ensure_ascii=False))
def batch_search(keywords, appid):
session = requests.Session()
done = load_done()
for kw in keywords:
if kw in done:
continue
try:
data = search_item(session, appid, kw)
# ここで保存処理(DB書き込み等)
except Exception as e:
print(f"failed: {kw} ({e})")
continue
done.add(kw)
save_done(done)
動的サイトやログイン必須サイトを含む取得基盤全体の設計パターンはスクレイピング実践テク|動的・ログイン攻略【26年8月】でも解説しているので、API以外の対象も扱う場合は合わせて参考にしてください。
ナイーブな実装との比較
項目ナイーブな実装本記事の設計
リクエスト間隔固定sleep(過不足あり)直近60秒の実績で動的制御
429発生時例外で処理全体が停止Retry-Afterに従い自動再試行
途中失敗時最初からやり直し処理済みログから再開
並列実行制限にすぐ到達appid単位で一元管理(並列化しない)
規約・法令面で必ず確認すべきこと
appidの複数取得による回避は行わない。1分30リクエスト制限を「複数のappidを使い分けて回避する」設計は、Yahoo!デベロッパーネットワークの利用規約における不正利用・目的外利用に該当するおそれがあります。制限は素直に守った上で、必要な取得量から逆算した設計にすべきです。
取得データの再配布・キャッシュ保持には利用規約上の制約があります。API経由で取得した商品情報を自社サービス内でどこまで保持・表示してよいかは、規約の該当条項を都度確認してください(改定される可能性があるため、実装時点の最新版を参照すること)。
商用利用時の表示要件(提供元表記など)が定められている場合があるため、画面に検索結果を表示するサービスを作る際は規約のUI要件も確認が必要です。
API経由の取得であっても、取得した価格・在庫情報をもとにした自動発注や自動出品を行う場合は、対象モール側の別規約(出店規約等)にも抵触しないか要確認です。
取得したデータの活用設計や大量データ処理の全体像はスクレイピング実装テクニック【2026年8月最新】でも扱っています。
自分で実装する場合の落とし穴と外注という選択肢
ここまでの実装は決して難しいコードではありません。しかし実務でハマるのは「動いているように見えて、実は制限ギリギリを綱渡りしていて数日後に突然止まる」「深夜バッチがエラーで止まったまま誰も気づかない」「規約改定に気づかず放置していた」といった運用フェーズの落とし穴です。特に複数モールのAPIを併用する価格差リサーチのような設計になると、モールごとに制限値もエラー仕様も異なり、統合的なリトライ・監視の設計コストが一気に上がります。自社でここまでの運用体制を組む工数と、外注コストを比較検討する価値は十分にあります。
この記事に関連するアイテム
実際に使うものを選ぶ際の参考にどうぞ。
Amazonで「スクレイピング」を探す
※ 本サイトはAmazonアソシエイト・プログラムの参加者です。紹介リンクを経由してご購入いただいた場合、手数料を受け取ることがあります。
まとめ
Yahoo!ショッピングAPI ItemSearchの1分30リクエスト制限は、固定sleepではなくスライディングウィンドウ方式のレートリミッターと、Retry-Afterに従った指数バックオフで確実に回避できます。加えて、処理済みログによる再開設計と、appid複数取得を行わないという規約遵守の姿勢が、長期運用できるバッチの条件です。実装自体は難しくなくても、監視・規約変更対応まで含めた運用は手間がかかります。業務自動化・スクレイピングの導入をご検討の方はnashiまでお問い合わせください。https://nashi-portfolio.netlify.app