以前このブログで「GitHub Actions から日本株の終値を取りたいなら stooq.com(apikey 経由)一択だった話」という記事を書きました。あれから少し経って、その結論が丸ごと使えなくなったので、続報として書き直します。結論から言うと、いまは Yahoo Finance のチャート API を直叩きするのが現実解です。
TL;DR
- stooq.com は 2026 年に全エンドポイントへ JavaScript の Bot チャレンジを導入し、CSV が返らなくなりました。apikey の発行ページごと閉じたので、再取得もできません。旧記事の結論は失効です。
- 代替を順に試した結果、Twelve Data 無料プランは東証(JPX)が有料限定、Google スプレッドシートの GOOGLEFINANCE は東証銘柄が
#N/Aで、どちらも脱落しました。 - 最終的に Yahoo Finance のチャート API(
query1/query2.finance.yahoo.com/v8/finance/chart/<銘柄コード>.T)を、標準ライブラリだけで直叩きする形に落ち着きました。API キー不要で、GitHub Actions のクラウド IP からも 429 なしで取れています。 - 「
yfinanceはクラウドだと 429 で死ぬ」というのは事実ですが、それはライブラリの重い挙動が原因で、単発の chart API を 1 回 GET するのとは別物です。ここを誤解して Yahoo 系を丸ごと避けていました。
背景:以前は stooq の apikey を勧めていた
作っているのは、自分が保有している日本株について「基準日(2026-02-08)の終値」と「本日の終値」の差分を、平日 17:00 JST に自分宛へメールする日次バッチです。実行基盤は GitHub Actions(schedule + 手動トリガ)、言語は Python、メール送信は Gmail の SMTP。要件で大事なのは次の点でした。
- 比較基準日は固定(前営業日ではない)。基準価格は JSON で保持。
- 土日は送らない。
- 株価取得に失敗してもメールは送る。本文に銘柄ごとのエラー原因を書く。
株価データ取得は、前回の記事では「ローカルは yfinance、クラウド本番は stooq の apikey 経由 CSV に差し替える」のがいちばんラク、と結論づけていました。実際、しばらくはそれで動いていたんです。
異変:stooq が CSV を返さなくなった
ある週から、届くメールの全銘柄が「取得失敗」になりました。本文に残しておいたエラー原因を見ると、CSV ではなく JavaScript の Bot チャレンジページがそのまま返ってきていました。
<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="robots" content="noindex,nofollow"></head><body>
<noscript>This site requires JavaScript to verify your browser.
Please enable JavaScript and reload.</noscript>
<script nonce="...">(async()=>{ ... proof-of-work ... })()</script>
切り分けてみると、apikey を付けても・付けなくても同じチャレンジ HTMLが返り、さらに apikey 取得ページ(?get_apikey)自体もチャレンジの向こう側に入っていました。ブラウザ(JS が動く環境)で開いても、以前のように apikey 文字列が表示されません。
つまり「apikey が失効した」どころではなく、新しい apikey を取り直すこと自体ができない状態です。GitHub Actions の curl / urllib は JS を実行できないので、この proof-of-work チャレンジは越えられません。stooq は自動バッチ用途からは完全に脱落、と判断しました。
試行 1:Twelve Data 無料 — 東証(JPX)は有料限定だった
次に、API キーで認証するタイプなら IP では弾かれないはず、と考えて Twelve Data を試しました。銘柄検索では確かに「2593 = Ito En, Ltd. / 取引所 JPX / JPY」とヒットします。期待して無料キーを取り、実際に quote エンドポイントを叩いたところ——
{"code":404,
"message":"This symbol is available starting with the Pro or Venture plan. Consider upgrading now at https://twelvedata.com/pricing",
"status":"error"}
無料プランは東証銘柄を返してくれません。JPX は Pro / Venture プラン限定でした。検索でヒットするのにデータは有料、というのはありがちな罠です。push する前にローカルで実キーを叩いて確認していたので、無駄な本番デプロイは避けられました。
試行 2:Google スプレッドシート(GOOGLEFINANCE) — 東証は #N/A
「Google のサーバ側で取得させて、ウェブに公開した CSV を GitHub Actions から読む」という proxy 作戦も考えました。シートに次のように書きます。
A1: 2593 B1: =GOOGLEFINANCE("TYO:"&A1,"price")
A2: 6367 B2: =GOOGLEFINANCE("TYO:"&A2,"price")
ところが B 列は #N/A。数式ミスなら #ERROR! や #NAME? になりますが、#N/A は「そのシンボルのデータを持っていない」の意味です。GOOGLEFINANCE は近年、海外取引所のカバレッジを縮小していて、東証銘柄が取れないのは既知の挙動でした。これも脱落です。
試行 3:Yahoo Finance チャート API 直叩き — これが現実解
ここまでで「無料で、東証の当日値を、クラウドのサーバから取れる」手段は実質ほぼ尽きました。残ったのが Yahoo Finance のチャート APIです。
https://query1.finance.yahoo.com/v8/finance/chart/2593.T?range=5d&interval=1d
返ってくる JSON のここに当日値(場が引けた後は終値)が入っています。
{"chart":{"result":[{"meta":{
"currency":"JPY","symbol":"2593.T",
"regularMarketPrice":2710,
"chartPreviousClose":2684.5,
...
}}],"error":null}}
「でも Yahoo 系は yfinance がクラウドで 429 連発するから避けたんじゃ?」——自分もそう思って遠回りしました。けれど、あの 429 は yfinance ライブラリがクッキー/クラム取得などで何度も叩く挙動が主因で、軽量な chart エンドポイントを 1 回 GET するのとは別物です。実際、GitHub Actions(workflow_dispatch)で回しても 429 は出ず、伊藤園 2593.T も ダイキン工業 6367.T も普通に取得・送信できました。
実装:Python標準ライブラリだけで叩く
追加パッケージは不要です。urllib と json だけで完結します。query1 が落ちても query2 に切り替えるホストフォールバックを入れておくと安定します。
import json
from urllib.request import Request, urlopen
HOSTS = ("query1.finance.yahoo.com", "query2.finance.yahoo.com")
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/124.0 Safari/537.36")
def get_price(symbol: str) -> float:
last_err = None
for host in HOSTS: # query1 がダメなら query2
url = f"https://{host}/v8/finance/chart/{symbol}?range=5d&interval=1d"
try:
req = Request(url, headers={"User-Agent": UA})
with urlopen(req, timeout=15) as resp:
data = json.loads(resp.read().decode("utf-8"))
except Exception as e: # 429 等はここで握って次のホストへ
last_err = e
continue
result = (data.get("chart") or {}).get("result") or []
if result:
return float(result[0]["meta"]["regularMarketPrice"])
raise RuntimeError(f"取得失敗: {last_err}")
print(get_price("2593.T")) # 伊藤園 -> 2710.0
銘柄リストは差し替え前提なので、コードに直書きせず JSON に外出ししています。Yahoo のティッカーは東証なら <銘柄コード>.T です。
[
{ "name": "伊藤園", "code": "2593", "yahooSymbol": "2593.T", "basePrice": 3054 },
{ "name": "ダイキン工業", "code": "6367", "yahooSymbol": "6367.T", "basePrice": 18010 }
]
そして何より気持ちがいいのが、GitHub Actions 側から株価用のシークレットが消えたことです。Yahoo は無キーなので、渡すのはメール関連だけになりました。
- name: Run kabumail
env:
SMTP_USER: ${{ secrets.SMTP_USER }}
SMTP_PASS: ${{ secrets.SMTP_PASS }}
MAIL_TO: ${{ secrets.MAIL_TO }}
# STOOQ_APIKEY / TWELVEDATA_APIKEY はもう不要
run: python src/kabumail.py
比較表
| 取得元 | 認証 | 東証カバー | クラウドIP(GHA)で動く | 料金 | 現状 |
|---|---|---|---|---|---|
| yfinance(ライブラリ) | 不要 | ○ | △(429 多発) | 無料 | ローカル試作向き |
| stooq 素叩き CSV | 不要 | ○ | ✕(Bot チャレンジ) | 無料 | 不可 |
| stooq apikey 経由 | apikey | ○ | ✕(発行停止・チャレンジ) | 無料 | 不可(再取得もできない) |
| Twelve Data 無料 | API キー | ✕(Pro/Venture 限定) | ○ | 無料 | 東証は取れない |
| GOOGLEFINANCE | 不要 | ✕(#N/A) | — | 無料 | 東証は取れない |
| Yahoo チャート API 直叩き | 不要 | ○ | ○(実測 OK) | 無料 | ◎ 現状の現実解 |
Yahoo チャート API を使うときの注意点
1. 非公式エンドポイントである
/v8/finance/chart/ は Yahoo Finance のフロントが使う非公開 API です。利用は自己責任で、節度を持って(1 日数回・少数銘柄程度に)。仕様やレート制限が予告なく変わる可能性は常にあります。
2. シンボルは <銘柄コード>.T、価格は meta.regularMarketPrice
東証は 2593.T のように .T を付けます。場が引けた後(自分のバッチは 17:00 JST 実行)なら regularMarketPrice がその日の終値になります。保険として indicators.quote[0].close の末尾値を見るようにしておくと、稀に meta 側が欠けても拾えます。
3. query1 がダメでも query2 がある
同じパスで query1 と query2 の 2 ホストが使えます。片方が一時的に不調でももう片方で取れることがあるので、ホストフォールバックを入れておくと安定します。
4. クラウド IP の 429 に備える
いまは GitHub Actions の IP でも通っていますが、データセンター IP が将来弾かれる可能性はゼロではありません。軽いリトライを入れつつ、ダメになったときの退避先(Google Apps Script で Yahoo を取得して CSV 公開する proxy 方式、または Twelve Data Pro 等の有料 API)も頭の片隅に置いておくと安心です。
5. 取得に失敗してもメールは止めない
これは取得元に関係なく大事な設計です。価格が取れなかった銘柄は「取得失敗」とその原因(HTTP エラーや JSON 構造の異常など)を本文に書いて、メール送信自体は必ず実行します。こうしておくと、今回のように取得元が静かに壊れたときも「届いたメールのエラー文」が一次情報になり、切り分けが一気に速くなります。実際、今回も本文に残していたチャレンジ HTML が原因特定の決め手でした。
結論:用途別の使い分け
- ローカルでの試作:
yfinanceが手軽。書き味がよく、すぐ動きます。 - クラウド(GitHub Actions 等)での本番:Yahoo チャート API を標準ライブラリで直叩き。無キーで、IP も今のところ問題なし。これが現状いちばんラクです。
- 業務でカッチリ・SLA が要る:素直に有料 API(Twelve Data Pro など)。非公式エンドポイント依存のリスクをお金で消せます。
前回は「stooq apikey 一択」と書きましたが、無料で登録不要な株価ソースはある日いきなり閉じるものだと、今回あらためて思い知りました。だからこそ「取得失敗してもバッチは止めない・原因をメールに残す」という土台が効いてきます。同じように GitHub Actions から日本株を取りたい人の遠回りを少しでも減らせれば幸いです。

コメント