yfinance が YFRateLimitError で動かないときの確認手順と、その背景
yfinanceを使って、米株を無料でダウンロードしていた人は、多いのではないでしょうか?そして、ある日突然、動いていたコードがエラーメッセージを表示し、ダウンロードできなくなった。
YFRateLimitError: Too Many Requests. Rate limited. Try after a while.
「リクエストの出しすぎ」というメッセージだが、1回目の呼び出しで出ている場合、原因はたいてい別のところにある。本記事では復旧手順を先に示し、その後でなぜこうなるのかを説明する。
まずバージョンの確認
import yfinance as yf
print(yf.__version__)
0.2.x が表示されたら、それが原因である可能性が高い。理由は後述するが、0.2.54 以前は Yahoo 側の遮断に対応できていない。
トレースバックからも判別できる。以下のように proxy 引数がシグネチャに並んでいたら旧バージョンである。
self._get_ticker_tz(self.proxy, timeout=10)
cache_get(self, url, user_agent_headers=None, params=None, proxy=None, timeout=30)
1.0 以降は proxy 設定が yf.config に移行しているため、この形は残っていない。
アップデートする
pip install -U yfinance curl_cffi
Jupyter / JupyterLab を使っている場合、ここで2つの罠がある。
罠1: カーネルを再起動していない
pip install を実行しても、すでに import 済みのモジュールは差し替わらない。Kernel → Restart を必ず実行する。これを忘れると、更新したのにバージョンが変わらないという現象が起きる。
罠2: pip の入れ先がカーネルと違う
ノートブックのカーネルと pip が別の環境を指していることがある。ノートブック内から実行するなら、確実に同じ環境へ入れるためこう書く。
import sys
!{sys.executable} -m pip install -U yfinance curl_cffi
再起動後、バージョンを再確認する。
import yfinance as yf
print(yf.__version__) # 1.5.2 など
まだ古いままなら、パスのズレを確認する。
import sys, yfinance
print(sys.executable)
print(yfinance.__file__)
動作確認
最小限のコードで通ることを確かめる。
import yfinance as yf
dat = yf.Ticker("AAPL")
df = dat.history(period="1mo", auto_adjust=True)
print(df.tail())
Open High Low Close \
Date
2026-08-05 00:00:00-04:00 309.093424 311.441405 305.406631 310.732025
2026-08-06 00:00:00-04:00 314.069138 316.017470 308.963556 312.140808
2026-08-07 00:00:00-04:00 311.181643 314.538733 310.472233 313.059998
2026-08-10 00:00:00-04:00 306.829987 308.260010 304.609985 308.260010
2026-08-11 00:00:00-04:00 307.750000 309.970001 305.670013 306.470001
Volume Dividends Stock Splits
Date
2026-08-05 00:00:00-04:00 49438800 0.00 0.0
2026-08-06 00:00:00-04:00 46139900 0.00 0.0
2026-08-07 00:00:00-04:00 34437200 0.00 0.0
2026-08-10 00:00:00-04:00 44775100 0.27 0.0
2026-08-11 00:00:00-04:00 8201653 0.00 0.0
auto_adjust は明示する。デフォルト値が過去に変更されており、指定しないと配当・分割調整の有無が環境依存になる。リターン計算をするなら死活問題になる。
日本株なら証券コードに .T を付ける。
dat=yf.Ticker("7203.T").history(period="1mo", auto_adjust=True) # トヨタ
dat.tail()
Open High Low Close Volume Dividends Stock Splits
Date
2026-08-04 00:00:00+09:00 2948.0 3028.0 2871.0 2918.5 70011500 0.0 0.0
2026-08-05 00:00:00+09:00 2903.0 2924.5 2869.5 2914.5 48003400 0.0 0.0
2026-08-06 00:00:00+09:00 2930.0 2993.5 2918.0 2983.5 31969900 0.0 0.0
2026-08-07 00:00:00+09:00 2995.0 3022.0 2964.5 2980.0 24563900 0.0 0.0
2026-08-10 00:00:00+09:00 2989.5 2989.5 2939.5 2981.0 23489400 0.0 0.0
dat=yf.download("USDJPY=X", period="1y") # ドル円
[*********************100%***********************] 1 of 1 completed
Price Close High Low Open Volume
Ticker USDJPY=X USDJPY=X USDJPY=X USDJPY=X USDJPY=X
Date
2026-08-05 157.692001 157.863007 157.302002 157.735001 0
2026-08-06 157.600006 158.479996 157.598999 157.664001 0
2026-08-07 158.408997 158.561005 156.830994 158.447998 0
2026-08-10 157.891006 159.046005 157.848999 157.899994 0
2026-08-11 159.227997 159.389999 158.919998 159.222000 0
それでも 429 が出る場合
cookie キャッシュを消す
古い cookie が残っていると復旧を妨げる。
- Windows:
%LOCALAPPDATA%\py-yfinance - macOS / Linux:
~/.cache/py-yfinance
ブラウザ偽装セッションを明示的に渡す
import yfinance as yf
from curl_cffi import requests
session = requests.Session(impersonate="chrome")
dat = yf.Ticker("AAPL", session=session)
print(dat.history(period="1mo"))
IP 単位で遮断されている場合は待つ
上記すべてで駄目なら、IP レベルでブロックされている可能性がある。過去の報告では数時間〜24時間以上続いた例もある。テザリングなど別回線で試すと切り分けられる。
大量取得は最初から前提を変える
銘柄ごとにループで Ticker を作るのは最も 429 を踏みやすい。まとめて取得し、待機を挟む。
import time
for chunk in chunks(tickers, 50):
df = yf.download(chunk, start=start, auto_adjust=True)
save(df)
time.sleep(2)
ここから背景: なぜ「使ってもいないのに」429 なのか
以下は動作復旧には不要だが、同じ問題を繰り返さないために知っておく価値がある。
yfinance は公式ライブラリではない
Ran Aroussi 氏によるオープンソースプロジェクトで、Yahoo, Inc. とは無関係である。README にも明記されている。
yfinance is not affiliated, endorsed, or vetted by Yahoo, Inc.
さらに重要なのは、Yahoo Finance の公式 API は2017年に提供終了しているという点だ。現在あるのは Yahoo Finance のサイト自身が内部で使うエンドポイントだけで、yfinance はそこを叩いている。契約に基づく安定供給ではなく、相手の都合でいつでも変わるインターフェースに追随し続けているプロジェクトである。
これが「定期的に壊れる」ことの構造的な理由になっている。
壊れ方の変遷
CHANGELOG を辿ると、時代ごとに攻防のテーマが変わってきたことが分かる。
2017年 — Selenium からの脱却
初期はブラウザ自動化ツールだった。0.0.2 に「ChromeDriver の場所を指定するオプション」があり、0.0.5 で「Selenium・PyVirtualDisplay・ChromeDriver 不要になった」と記録されている。0.1.36 で fix-yahoo-finance から yfinance に改名された。
2022〜2023年 — 暗号化との消耗戦
Yahoo がページ埋め込みの JSON を暗号化した。CHANGELOG の並びがそのまま疲弊の記録になっている。
0.2.4 Fix Yahoo data decryption
0.2.5 Fix Yahoo data decryption again
0.2.7 Fix Yahoo decryption, smarter this time
0.2.10 add another backup decrypt option
決着は 0.2.14。HTML の復号をやめ、API エンドポイントから直接取得する方式へ移行した。以降この種の問題は再発していない。
2023年 — cookie と crumb
Yahoo が CSRF トークン(crumb)を導入。0.2.32 で cookie & crumb 対応が入った。今でもデバッグログを有効にするとこの手順が見える。
2025年 — TLS フィンガープリントによる遮断(今回の原因)
2025年2月、Yahoo Finance がサイトを刷新し、新しいクォータ制を導入した。4月には YFRateLimitError の報告が GitHub に殺到する。特徴的だったのは、報告者が口を揃えて**「IP を変えても直らない」**と述べていた点だ。
正体は JA3 フィンガープリントによる識別だった。TLS ハンドシェイク時に送られる暗号スイートの組み合わせや拡張の順序は、クライアント実装ごとに固有のパターンを持つ。Python の requests(OpenSSL ベース)と実際の Chrome では、これが明確に異なる。User-Agent を偽装しても無意味である。ヘッダより下の層で判別されているからだ。
対策として採用されたのが curl_cffi で、これは実際のブラウザの TLS ライブラリ(Chrome なら BoringSSL)を使い、フィンガープリントごと模倣する。
この時期の CHANGELOG は正直だ。
0.2.58 Fix false rate-limit problem
0.2.59 Fix the fix for rate-limit
0.2.60 Fix cookie reuse, DNS blocking fc.yahoo.com への対応
0.2.62 crumb取得時のレート制限検出 / curl_cffi への置き換え
"Fix the fix for rate-limit" ——修正の修正——という一行が、当時の状況を何より雄弁に表している。
0.2.54 が最悪の位置にある理由
冒頭で「0.2.54 以前は対応できていない」と書いた根拠がここにある。
0.2.54 は 2025年2月、つまりサイト刷新の直後に出たリリースである。上に並べた対策(0.2.58〜0.2.62)はすべてその後に入った。TLS フィンガープリント対策も、cookie 再利用の修正も、crumb 取得段階のハンドリングも含まれていない。
だから 0.2.54 は、Yahoo の遮断に対して丸腰である。一発目のリクエストで 429 が返るのは「使いすぎ」ではなく、リクエストの出し方そのものが「ブラウザではない」と判定されているためだ。
エラーメッセージが Too Many Requests なので使用量の問題と誤解しやすいが、実態は識別による遮断である。ここが今回の要点になる。
その後の経緯
- 2025年12月 バージョン 1.0 到達。リリースノートいわく「yfinance been stable a long time now, time to grow up」
-
2026年4月 CVE 対応で
curl_cffi>=0.15を強制 -
2026年5月 1.4.0 で curl_cffi をオプション化、
requestsへのフォールバックを追加 - 2026年7月23日 1.5.2 で「curl_cffi 0.16 以降での破損」を修正
最後の 1.5.2 は興味深い。原因が Yahoo 側ではなく依存パッケージのバージョンアップだった。レート制限を回避するために導入した curl_cffi が、今度はそれ自体が破損要因になったという皮肉な展開である。
実務上の指針
バージョンを固定する。ただし依存も含めて。
yfinance==1.5.2
curl_cffi>=0.15,<0.16
1.5.2 の件が示す通り、本体だけ固定しても不十分である。pip freeze や uv lock で環境全体を固定するのが確実だ。
取得したデータは必ずローカルに保存する。
from pathlib import Path
import pandas as pd, yfinance as yf
CACHE = Path("data/prices.parquet")
if CACHE.exists():
df = pd.read_parquet(CACHE)
else:
df = yf.download(tickers, start="2015-01-01", auto_adjust=True)
CACHE.parent.mkdir(exist_ok=True)
df.to_parquet(CACHE)
Yahoo は過去データを遡って修正することがある。取得と分析を分離しておかないと、再現性が保てない。
構造的な制約は把握しておく。
- 分足は 1m で7日、それ以外でも60日程度しか遡れない
- 上場廃止銘柄が取得できないため、現在の構成銘柄で過去を分析すると生存バイアスが入る
- 利用規約上は個人利用が前提。商用利用はグレー
マクロ指標や金利なら FRED(fredapi)や日銀の時系列統計、有料でよければ Polygon.io、Tiingo、EOD Historical Data という選択肢がある。無料の代替では Stooq も使える。
まとめ
-
YFRateLimitErrorが1回目の呼び出しで出るなら、使いすぎではなくバージョンが古い可能性が高い - Jupyter では
pip install後のカーネル再起動を忘れない - 0.2.5x 系は2025年春の遮断に対応していない。1.x 系に上げる
- このライブラリは公式 API ではなく、壊れることを前提に設計するのが正しい付き合い方
参考
※2026年8月時点の情報です。
参考
Python3ではじめるシステムトレード【第2版】環境構築と売買戦略
「画像をクリックしていただくとpanrollingのホームページから書籍を購入していただけます。