日本株の検証を、実測で。

J-Quants API の V1 は終了しました|V2 でコードのどこが壊れるか

  • 2026年9月21日
  • 2026年9月21日
  • 証券API

ネットに出ている J-Quants のサンプルコードをそのまま動かして、認証で止まる・403 が返る・キーエラーで落ちる——これは書いた人が間違えたのではなく、API が作り替わったためです。

  • V1 は 2026年6月1日に終了しました。いま動くのは V2 だけです
  • 認証がトークン方式から APIキー方式に変わりました
  • エンドポイントが全部変わりました(対応表を載せています)
  • レスポンスのカラム名が短縮形になりました。Close が C です。ここが一番静かに壊れます
  • Premium の取得期間が「無制限」から「過去20年」に縮みました

まず結論:V1 はもう存在しません

J-Quants API は V1 から V2 へ移行し、旧バージョンの V1 は 2026年6月1日に終了しました。2025年12月22日以降に登録した人は、はじめから V2 しか使えません。

つまり、2025年までに書かれた解説記事・サンプルコード・ライブラリの多くは、そのままでは動きません。検索で上位に出てくるものほど古いことがあるので、まずここを疑ってください。

この記事は公式ドキュメントの内容を整理したものです。実際に叩いて測った話(1リクエストあたりの速度、429がどのくらいで飛んでくるか、ティックCSVの実サイズなど)は、別の記事で出します。どこまでが仕様の話で、どこからが実測かは、記事ごとにはっきり分けます。

変わったところ(5つ+1)

1. 認証:トークン方式 → APIキー方式

V1 では、メールアドレスとパスワードでリフレッシュトークンを取り、それで ID トークンを取り、ID トークンを Authorization: Bearer に載せる——という3段構えでした。V2 ではこれが全部なくなります。

ダッシュボードで発行した APIキーを x-api-key ヘッダーに入れるだけです。APIキー自体に有効期限はありません(再発行と削除はできます)。トークンの期限切れを気にしてリフレッシュ処理を書いていた部分は、まるごと消せます。

v1_old.py
# V1 のコード(もう動かない)
import requests

# 1. メールアドレスとパスワードでリフレッシュトークンを取る
r = requests.post(
    "https://api.jquants.com/v1/token/auth_user",
    json={"mailaddress": MAIL, "password": PASS},
)
refresh_token = r.json()["refreshToken"]

# 2. リフレッシュトークンから ID トークンを取る
r = requests.post(
    "https://api.jquants.com/v1/token/auth_refresh",
    params={"refreshtoken": refresh_token},
)
id_token = r.json()["idToken"]

# 3. ID トークンを Authorization ヘッダーに載せて叩く
r = requests.get(
    "https://api.jquants.com/v1/prices/daily_quotes",
    params={"code": "8697", "date": "2026-09-18"},
    headers={"Authorization": f"Bearer {id_token}"},
)
quotes = r.json()["daily_quotes"]
print(quotes[0]["Close"])
v2_new.py
# V2 のコード
import requests

API_KEY = "..."  # ダッシュボードの [API Keys] で発行する

r = requests.get(
    "https://api.jquants.com/v2/equities/bars/daily",
    params={"code": "8697", "date": "2026-09-18"},
    headers={"x-api-key": API_KEY},
)
r.raise_for_status()
rows = r.json()["data"]      # V1 の "daily_quotes" ではなく "data"
print(rows[0]["C"])          # V1 の "Close" ではなく "C" 

2. エンドポイントが全部変わった

パスの付け方が「データの種類ごとの階層」に整理され直しました。対応表は次のとおりです。

データV1V2
トークン発行/v1/token/auth_user廃止(APIキーを使う)
トークンリフレッシュ/v1/token/auth_refresh廃止(APIキーを使う)
株価四本値/v1/prices/daily_quotes/v2/equities/bars/daily
前場四本値/v1/prices/prices_am/v2/equities/bars/daily/am
上場銘柄一覧/v1/listed/info/v2/equities/master
決算発表予定日/v1/fins/announcement/v2/equities/earnings-calendar
投資部門別情報/v1/markets/trades_spec/v2/equities/investor-types
財務情報/v1/fins/statements/v2/fins/summary
財務諸表(BS/PL/CF)/v1/fins/fs_details/v2/fins/details
配当金情報/v1/fins/dividend/v2/fins/dividend
取引カレンダー/v1/markets/trading_calendar/v2/markets/calendar
売買内訳データ/v1/markets/breakdown/v2/markets/breakdown
信用取引週末残高/v1/markets/weekly_margin_interest/v2/markets/margin-interest
日々公表信用取引残高/v1/markets/daily_margin_interest/v2/markets/margin-alert
業種別空売り比率/v1/markets/short_selling/v2/markets/short-ratio
空売り残高報告/v1/markets/short_selling_positions/v2/markets/short-sale-report
指数四本値/v1/indices/v2/indices/bars/daily
TOPIX指数四本値/v1/indices/topix/v2/indices/bars/daily/topix
先物四本値/v1/derivatives/futures/v2/derivatives/bars/daily/futures
オプション四本値/v1/derivatives/options/v2/derivatives/bars/daily/options
日経225オプション四本値/v1/option/index_option/v2/derivatives/bars/daily/options/225

見てのとおり、機械的に置換できるものはほとんどありません。/v1//v2/ に変えるだけでは動きません。

3. レスポンスが data キーに統一された

V1 はエンドポイントごとに入れ物の名前が違いました(daily_quotesinfostatements など)。V2 は原則としてすべて data の配列で返ってきます。

{
  "data": [
    { ... },
    { ... }
  ],
  "pagination_key": "..."
}

該当データが0件のときもエラーにはならず、200{"data": []} が返ります。pagination_key は付きません。

4. カラム名が短縮形になった(ここが一番危ない)

認証やエンドポイントの変更は、動かないのですぐ気づきます。怖いのはこちらです。

項目V1V2
始値OpenO
高値HighH
安値LowL
終値CloseC
出来高VolumeVo
売買代金TurnoverValueVa
調整後始値AdjustmentOpenAdjO
調整後高値AdjustmentHighAdjH
調整後安値AdjustmentLowAdjL
調整後終値AdjustmentCloseAdjC
調整後出来高AdjustmentVolumeAdjVo
調整係数AdjustmentFactorAdjFactor
日付DateDate(変わらず)
銘柄コードCodeCode(変わらず)

これが静かに壊れる理由。たとえば row.get("Close") と書いていたコードは、V2 では例外を出さずに None を返します。None のまま計算に流れ込めば、「バックテストは通ったのに成績がおかしい」という形で後から出てきます。移行のときは .get() ではなく [] で取り、キーが無ければその場で落とすほうが安全です。

5. レートリミットが新設された

V1 には明示のレートリミットがありませんでした。V2 ではプランごとに上限が決まっています。

プラン上限(リクエスト/分)
Free5
Light60
Standard120
Premium500

加えて、財務情報/v2/fins/summary)と財務諸表/v2/fins/details)は、プランに関係なく専用枠で 60/分。分足・ティックのアドオンも専用枠で 60/分です。

上限を超えると 429 が返ります。Retry-After ヘッダーは付きません。判定は1分のスライディングウィンドウで、429 になったリクエストも回数に数えられるため、待たずに投げ直すと制限が解けるまでの時間が延びます。大幅に超過し続けると、5分程度アクセスが完全に遮断されることがあります。

retry.py
import time
import requests


def get_with_retry(url, params, headers, tries=5):
    for i in range(tries):
        r = requests.get(url, params=params, headers=headers, timeout=60)
        if r.status_code == 429:
            # Retry-After は付かない。1分のスライディングウィンドウなので
            # 「すぐ投げ直さず、余裕をみて待つ」以外に手はない
            time.sleep(90)
            continue
        if r.status_code in (500, 502, 503, 504):
            time.sleep(5 * (i + 1))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError(f"{tries} 回試しても取れなかった: {url} {params}")

おまけ:Premium の取得期間が縮んだ

見落とされがちですが、Premium プランの期間制限が「無制限」から「過去20年分」に変わりました。20年より前まで遡る検証を組んでいた場合は、前提が変わります。

逆に緩くなった点もあり、上場銘柄一覧の貸借信用区分は、V1 では Standard 以上だったものが V2 では全プランで取れるようになりました。

ページングの書き方

レスポンスが大きいと pagination_key が付いて返ってきます。付かなくなるまで繰り返すのが全件取得の作法です。総件数を返す項目はありません。

fetch_all.py
import requests

HEADERS = {"x-api-key": API_KEY}
URL = "https://api.jquants.com/v2/equities/bars/daily"


def fetch_all(**params):
    out = []
    while True:
        r = requests.get(URL, params=params, headers=HEADERS)
        r.raise_for_status()
        body = r.json()
        out += body["data"]
        key = body.get("pagination_key")
        if not key:
            return out
        params["pagination_key"] = key   # 他の条件は変えないこと
  • 分割が起きるのは、レスポンスが 5,000,000 バイト相当に達したとき
  • 1回あたりの件数は内部処理で変動し、非公開・保証対象外
  • pagination_key の値は毎回変わる
  • 繰り返しの途中でデータが更新された場合、取得結果全体の一貫性は保証されない

403 が返ったときの読み分け

V2 の 403 は複数の原因で返ります。メッセージを見ないと切り分けられません。

メッセージ/状況原因
The incoming api key is invalid or expired.APIキーの値そのものが認識できない
メッセージなしで 403契約プランに含まれていないデータ・エンドポイントを叩いている
特定のエンドポイントだけ 403URL か HTTP メソッドの誤り。V1 のパスを叩いている場合もここ

V1 のパスを叩くと 404 ではなく 403 が返ることがあります。「権限の問題かと思ってプランを上げたが直らない」という遠回りをしやすいので、まず自分が叩いている URL が /v2/ かを確かめてください。

移行チェックリスト

  1. ダッシュボードの [API Keys] でAPIキーを発行する
  2. auth_user / auth_refresh を呼んでいる箇所をすべて削除する
  3. Authorization: Bearerx-api-key に置き換える
  4. エンドポイントを対応表で1つずつ書き換える(機械置換はできない
  5. レスポンスの取り出しを data に統一する
  6. カラム名を短縮形に直す。.get() ではなく [] で取って、抜けていればその場で落とすようにする
  7. 429 を待って再開する処理を入れる(Retry-After は来ない)
  8. pagination_key が付かなくなるまで繰り返すようにする
  9. Premium を使っていて20年より前を見ていた場合は、検証期間の前提を見直す

Windows で叩くときの細かい注意

公式ドキュメントにも注記がありますが、PowerShell では2つ引っかかります。

  • シングルクォートの中で変数が展開されません。ヘッダーはダブルクォートで書きます(-H "x-api-key: $key"
  • curl が別コマンドの別名になっていることがあります。curl.exe と明示してください
curl.exe -G "https://api.jquants.com/v2/equities/bars/daily" `
  -H "x-api-key: $key" `
  -d code=8697 `
  -d date=2026-09-18

まとめ

V1 から V2 への移行は、「認証を差し替えれば済む」という規模ではありません。エンドポイント・レスポンス構造・カラム名の3つが同時に変わっているので、データを取る層はほぼ書き直しになります。

そのうえで、いちばん怖いのはカラム名です。認証もパスも直して「動いた」と思ったあとに、Close が取れていなかったことに気づく——という順番になりがちだからです。検証の数字がおかしいと感じたら、まずそこを見てください。

この記事の出どころ

  • 情報源:J-Quants API 公式ドキュメント (V1 API から V2 API への変更点/クイックスタート/レートリミットについて/レスポンスステータス/レスポンスのページングについて)
  • 参照日:2026年9月20日
  • この記事の性格:公式ドキュメントの整理です。実際に叩いて測った数字は含みません
  • 動作確認:未実施。実測にもとづく記事は別途公開します