Strasmore Research
学習 Matt Connor著者: Matt Connor ・更新: 2026-10-06 · data as of October 6, 2026 · refreshed weekly

Pythonで無料の株式市場データAPIを使う方法

requestsとpandasで無料の株式市場データAPIを呼び出すUbuntuチュートリアルです。JSONをDataFrameに読み込み、単一のtickerの20セッション平均出来高を表示します。

Pythonから無料の株式市場データAPIを呼び出すには、HTTPリクエスト一回と、多くの人がすでに導入している二つのライブラリがあればよい。requestsでJSONを取得し、pandasでDataFrameに変換する。APIキーは必要ない。この手順は、まっさらなUbuntuコンテナ上で最初から最後まで実行でき、最後に単一のtickerについて20セッションの平均出来高を出力する。エンドポイントが提供するデータについては、無料の株式市場データAPIガイドで説明している。このページでは、同じAPIのPython側を扱う。

新しいUbuntuコンテナでPython、requests、pandasを設定する

標準のUbuntuイメージにはPythonのvenvモジュールが含まれておらず、最近のリリースではシステムのインタープリターへのパッケージのインストールも拒否されます。仮想環境を使えば、両方の問題を回避できます。現在のUbuntuリリース間で同じように動作します。以下のコマンドはrootとして実行するか、apt-getの行の先頭にsudoを付けてください。

export DEBIAN_FRONTEND=noninteractive
apt-get update && apt-get install -y python3 python3-venv
python3 -m venv .venv
. .venv/bin/activate
pip install "requests>=2.31,<3" "pandas>=2.0,<4"

バージョン範囲は意図的に設定されています。いずれもライブラリの保守対象となっているリリースを受け入れ、動作が未知の将来のメジャーバージョンだけを除外します。そのため、UbuntuがデフォルトのPythonを更新しても、同じ行で問題なくインストールできます。現在たまたま存在するビルドに固定しているわけではありません。以下はすべて、環境が有効になっていることを前提としています。

Pythonで無料の株式市場データAPIを呼び出す方法

デモ用エンドポイントは、キーや登録なしで通常のGETリクエストに応答します。最初のスクリプトは、用意されたクエリの一つを指定し、返されるデータの構造を表示します。

import json
import requests

BASE = "https://ai.strasmore.com/api/demo"

resp = requests.get(BASE, params={"q": "dividend_yield_leaders"}, timeout=30)
resp.raise_for_status()
payload = resp.json()

print(list(payload.keys()))
print(payload["columns"])
print(json.dumps(payload["rows"][0], indent=2))

requests.getはparamsからクエリ文字列を作成し、raise_for_status()は4xxまたは5xxのステータスを例外に変換します。.json()は本文を辞書に解析します。構造だけを示すと、この辞書は次のようになります。

{
  "key":     "dividend_yield_leaders",
  "label":   "...",
  "nl":      "Which large-cap US stocks currently have the highest dividend yields?",
  "sql":     "SELECT ...",
  "columns": ["as_of", "ticker", "dividend_yield_pct", "price", "market_cap_bn", "price_to_earnings"],
  "rows":    [{"as_of": "<date>", "ticker": "<symbol>", "dividend_yield_pct": <number>, ...}, ...],
  "elapsed": "...",
  "source":  "Strasmore Research",
  "more":    "..."
}

pandasで重要なのは二つのキーです。columnsはフィールド名を順番に示し、rowsは各レコードを、これらの名前をキーとするJSONオブジェクトとして保持します。sqlは数値の生成に使われた正確なクエリで、すべてのレスポンスに返されます。そのため、データはブラックボックスではなく、監査可能です。/api/demo/catalogへのGETリクエストでは、用意されたすべてのキーと、次に使うSQLエンドポイントが一覧表示されます。

JSONをpandas DataFrameに読み込む方法

キュレーション済みクエリは、固定された質問に答えるものです。選択したティッカーの日次バーは、/api/demo/sqlの登録不要SQLエンドポイントから取得できます。このエンドポイントはsqlパラメーターで読み取り専用クエリを受け取り、同じcolumnsとrowsの組み合わせを返します。2026年9月時点の制限は、すべてのレスポンスに記載されています。500行、20秒、過去1年分、APIキー不要です。キーを使う無料SQL APIでは、より長い期間を対象に、1日100クエリまで上限が引き上げられます。リクエストコードは同一です。

SQLには、平均値を正確に保つためのガードが2つあります。セッション中に更新されるテーブルには、当日分の行が複数含まれることがあります。また、市場の取引時間中は当日の出来高が未確定です。クエリはdate < today()で前日までに限定し、GROUP BY dateとmax(volume)で同一日の重複行を集約します。日次バーがテープからどのように作成されるかを確認していない場合は、OHLCVバーの作成方法で、その行の生成元を説明しています。

import time
import requests
import pandas as pd

SQL_URL = "https://ai.strasmore.com/api/demo/sql"
TICKER = "AAPL"

SQL = f"""
SELECT date, max(volume) AS volume
FROM stocks_daily_aggs
WHERE ticker = '{TICKER}'
  AND date < today()
GROUP BY date
ORDER BY date DESC
LIMIT 20
"""


def fetch(sql, attempts=4):
    """GET the SQL endpoint. Back off on 429 and 5xx; stop on any other 4xx."""
    delay = 2.0
    for attempt in range(1, attempts + 1):
        resp = requests.get(SQL_URL, params={"sql": " ".join(sql.split())}, timeout=30)
        if resp.status_code == 200:
            return resp.json()
        if resp.status_code == 429 or resp.status_code >= 500:
            try:
                wait = max(1.0, float(resp.headers.get("Retry-After")))
            except (TypeError, ValueError):
                wait = delay
            print(f"HTTP {resp.status_code}; waiting {wait:.0f}s (attempt {attempt} of {attempts})")
            time.sleep(wait)
            delay *= 2
            continue
        raise SystemExit(f"HTTP {resp.status_code}: {resp.text[:400]}")
    raise SystemExit("gave up after repeated 429 or 5xx responses")


payload = fetch(SQL)
df = pd.DataFrame(payload["rows"], columns=payload["columns"])
df["date"] = pd.to_datetime(df["date"])
df["volume"] = pd.to_numeric(df["volume"])
df = df.sort_values("date").reset_index(drop=True)

print(df.to_string(index=False))

avg_20 = df["volume"].mean()
first, last = df["date"].iloc[0], df["date"].iloc[-1]
print(f"{TICKER} 20-session average volume: {avg_20 / 1e6:.1f}M shares "
      f"({first:%Y-%m-%d} to {last:%Y-%m-%d}, {len(df)} sessions)")

pd.DataFrame(rows, columns=columns)はオブジェクトのリストから直接テーブルを作成し、columnsを渡すことでAPIのフィールド順を維持します。その後、2つの変換を行います。日付は文字列で届くため、実際のタイムスタンプに変換します。また、レスポンスがvolumeをテキストとしてシリアライズした場合に備え、数値へ変換します。古い順に並べ替えると、チャートで扱いやすい順序になります。ちょうど20行に対するdf["volume"].mean()が20セッション平均です。最後の行では、その平均を、対象期間の日付範囲とともに百万単位で表示します。

20日平均出来高は実際に何を測っているのか?

平均出来高は、日次データのノイズを平滑化する。1セッションの出来高は、指数のリバランス、オプションの満期、決算発表日、ヘッドラインによって大きく変動する。20セッションは暦でおよそ1カ月に相当し、こうした急増を抑えるには十分な長さである一方、銘柄の売買活況度の変化を追うには短い。以下のパネルは、スクリプトが出力するものと同じ統計値を、同じ日次テーブルから直近数カ月について計算したものだ。実績値と移動平均を横に並べている。

クエリAAPLの日次出来高と直近20セッション平均(百万株)
34 rows (showing 20)
セッション出来高(百万)20日平均(百万)
2026-08-1738.251.9
2026-08-1853.452.5
2026-08-1950.553
2026-08-204153.1
2026-08-2146.953
2026-08-2434.752.3
2026-08-2525.951
2026-08-263449.9
2026-08-2732.447.8
2026-08-2838.643.1
2026-08-3141.241.4
2026-09-0153.240.6
2026-09-0233.839.8
2026-09-0337.239.4
2026-09-0439.639.7
2026-09-0835.539.2
2026-09-0965.640.6
2026-09-107042
2026-09-1150.742.5
2026-09-1439.343.1
各数値の背後にある正確なSQL
SELECT
    session,
    volume_millions,
    avg_20d_millions
FROM
(
    SELECT
        toString(d)                                                                            AS session,
        round(vol / 1e6, 1)                                                                    AS volume_millions,
        round(avg(vol) OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) / 1e6, 1)  AS avg_20d_millions,
        count() OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW)                    AS sessions_in_window
    FROM
    (
        SELECT
            date                    AS d,
            toFloat64(max(volume))  AS vol
        FROM global_markets.stocks_daily_aggs
        WHERE ticker = 'AAPL'
          AND date >= today() - 75
          AND date <  today()
        GROUP BY date
    )
)
WHERE sessions_in_window = 20
ORDER BY session
自分で実行する

日次系列は各セッションにつき1つの値を示す。平滑化した系列は、そのセッションと直前の19セッションの平均である。パネルの期間は2026-08-17から2026-10-02までで、そこには20セッション分の完全な計算期間を持つ34セッションが含まれる。最後のセッションでAAPLの出来高は33.3百万株、20セッション平均は42.2百万株だった。この平均値が、このページの生成日にスクリプトが出力した数値である。今日実行すれば、対象となる20セッションが移動し、それに伴って数値も変わる。

ティッカーの入れ替え

TICKERを変更すれば、このスクリプトは表にある米国上場銘柄のティッカーに対応します。以下の比較では、4つの著名銘柄について同じ20セッションの計算を実行しています。指数ETFと3つのメガキャップで、「平均出来高」がどの程度の規模を指すのかを簡単に確認できます。

クエリ4つの代表的ティッカーの直近20セッション平均出来高(百万株)
ticker20日平均(百万)初回セッション最終セッション
NVDA1102026-09-042026-10-02
SPY462026-09-042026-10-02
AAPL42.22026-09-042026-10-02
MSFT21.32026-09-042026-10-02
各数値の背後にある正確なSQL
SELECT
    ticker,
    round(avg(vol) / 1e6, 1)   AS avg_20d_millions,
    toString(min(d))           AS first_session,
    toString(max(d))           AS last_session
FROM
(
    SELECT
        ticker,
        d,
        vol,
        row_number() OVER (PARTITION BY ticker ORDER BY d DESC) AS rn
    FROM
    (
        SELECT
            ticker,
            date                    AS d,
            toFloat64(max(volume))  AS vol
        FROM global_markets.stocks_daily_aggs
        WHERE ticker IN ('SPY', 'NVDA', 'AAPL', 'MSFT')
          AND date >= today() - 45
          AND date <  today()
        GROUP BY ticker, date
    )
)
WHERE rn <= 20
GROUP BY ticker
HAVING count() = 20
ORDER BY avg_20d_millions DESC
自分で実行する

4銘柄のうち、20セッション平均が最も大きいのはNVDAで、1セッション当たり110百万株です。最も小さいのはMSFTで、21.3百万株です。いずれも2026-09-04から2026-10-02までのセッションを対象に測定しています。ここでのAAPLの数値は、上のトレースが最後に示す数値と同じです。定義は一つで、一度計算した値を表示箇所にかかわらず使用しています。

429はどのような形で返り、どう丁寧に再試行すればよいか

レート制限は、データではなくHTTPステータスコードとして返されます。レスポンスにはステータス429 Too Many Requestsが含まれ、待機すべき秒数を示すRetry-Afterヘッダーが付くこともあります。本文は要求したデータではありません。そのため、fetchヘルパーは解析前にstatus_codeを確認します。判定ルールは次のとおりです。

  1. 200:JSONを解析して返します。
  2. 429または任意の5xx:Retry-Afterがあればその時間だけ待ちます。なければ、2秒、4秒、8秒、16秒の順に待機時間を延ばして再試行します。合計4回まで試行します。
  3. その他の4xx:処理を停止します。本文には、存在しないテーブルや読み取り専用ゲートが拒否したステートメントなど、クエリが拒否された理由が明記されています。再試行しても結果は変わりません。例えば、存在しないテーブルを指定した場合は400として返されます。

制限を回避するには、二つの点が重要です。キュレーション済みの結果は、更新が最短でも10分間隔です。それより短い間隔でループが取得しても同じバイト列になるため、ペイロードをローカルにキャッシュしても費用はかかりません。必要なデータだけを取得してください。例えば、20セッションの平均にはLIMIT 20を使い、破棄する1年分の行を取得する必要はありません。接続切断やDNS障害などのネットワーク障害はrequests.RequestExceptionとして発生し、ヘルパーでは捕捉されません。スクリプトを無人で実行する場合は、呼び出しをtry/exceptで囲んでください。

よくある質問

Python向けの無料の株式市場データAPIはありますか?

あります。このガイドのデモ用エンドポイントは、requestsからの認証不要のGETリクエストに対し、pandasへ直接読み込めるJSONを返します。/api/demo?q=<key>の厳選クエリと、/api/demo/sqlに記述した読み取り専用SQLを利用でき、取得件数は500行、履歴は1年分までです。APIキーも登録も必要ありません。

Pythonで株式データを取得するにはAPIキーが必要ですか?

ここで使うデモ階層では必要ありません。無料キーを取得すると、上限が1日100クエリに引き上げられ、より長い履歴を取得できます。Pythonコードは変更不要です。同じエンドポイントをLLM向けのツールとして組み込む開発者は、AIエージェント向けの市場データスキルから始められます。

JSON APIのレスポンスをpandasのDataFrameに変換するにはどうすればよいですか?

resp.json()で本文を解析し、行オブジェクトのリストをpd.DataFrame(rows, columns=columns)に渡します。算術処理の前に、日付文字列をpd.to_datetimeで、数値文字列をpd.to_numericで変換してください。JSONには日付型がなく、APIによっては小数をテキストとしてシリアライズするためです。

株式APIを呼び出した際の429エラーは何を意味しますか?

HTTP 429はToo Many Requestsを意味し、サーバーが呼び出し元をレート制限しています。Retry-Afterヘッダーが返される場合は、そこに示された秒数だけ待ってください。ヘッダーがない場合は、指数バックオフを行います。また、10分ごとにしか更新されない結果を繰り返し取得せず、レスポンスをキャッシュしてください。

pandasで平均出来高を計算するにはどうすればよいですか?

volume列を含む、セッションごとに一行のデータを読み込み、直近の完了済み20セッションに絞ってからdf["volume"].mean()を呼び出します。より長い履歴で移動平均を計算する場合はdf["volume"].rolling(20).mean()を使用します。上のトレースパネルもこれを描画しています。


上の各パネルには、対応する正確なSQLがその下に掲載されています。スクリプトも同じ考え方で、先頭にrequestsの呼び出しを加えています。無料のAPIキーで、1日100クエリ、22年分の履歴、raw tapeに対するSQLを利用できます。カード情報は不要です。APIキーを取得する

#python#stock market api#free market data#requests#pandas#tutorial