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カ月に相当し、こうした急増を抑えるには十分な長さである一方、銘柄の売買活況度の変化を追うには短い。以下のパネルは、スクリプトが出力するものと同じ統計値を、同じ日次テーブルから直近数カ月について計算したものだ。実績値と移動平均を横に並べている。
| セッション | 出来高(百万) | 20日平均(百万) |
|---|---|---|
| 2026-08-17 | 38.2 | 51.9 |
| 2026-08-18 | 53.4 | 52.5 |
| 2026-08-19 | 50.5 | 53 |
| 2026-08-20 | 41 | 53.1 |
| 2026-08-21 | 46.9 | 53 |
| 2026-08-24 | 34.7 | 52.3 |
| 2026-08-25 | 25.9 | 51 |
| 2026-08-26 | 34 | 49.9 |
| 2026-08-27 | 32.4 | 47.8 |
| 2026-08-28 | 38.6 | 43.1 |
| 2026-08-31 | 41.2 | 41.4 |
| 2026-09-01 | 53.2 | 40.6 |
| 2026-09-02 | 33.8 | 39.8 |
| 2026-09-03 | 37.2 | 39.4 |
| 2026-09-04 | 39.6 | 39.7 |
| 2026-09-08 | 35.5 | 39.2 |
| 2026-09-09 | 65.6 | 40.6 |
| 2026-09-10 | 70 | 42 |
| 2026-09-11 | 50.7 | 42.5 |
| 2026-09-14 | 39.3 | 43.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つのメガキャップで、「平均出来高」がどの程度の規模を指すのかを簡単に確認できます。
| ticker | 20日平均(百万) | 初回セッション | 最終セッション |
|---|---|---|---|
| NVDA | 110 | 2026-09-04 | 2026-10-02 |
| SPY | 46 | 2026-09-04 | 2026-10-02 |
| AAPL | 42.2 | 2026-09-04 | 2026-10-02 |
| MSFT | 21.3 | 2026-09-04 | 2026-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 DESC4銘柄のうち、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を確認します。判定ルールは次のとおりです。
200:JSONを解析して返します。429または任意の5xx:Retry-Afterがあればその時間だけ待ちます。なければ、2秒、4秒、8秒、16秒の順に待機時間を延ばして再試行します。合計4回まで試行します。- その他の
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キーを取得する