Strasmore Research
학습 Matt Connor작성자 Matt Connor · 업데이트됨 2026-10-07 · data as of October 7, 2026 · refreshed weekly

파이썬 무료 주식 데이터 API 호출 방법

Python의 requests와 pandas 라이브러리를 사용하여 무료 주식 시장 데이터 API를 호출하는 방법을 설명합니다. Ubuntu 환경에서 JSON 데이터를 DataFrame으로 변환하고 20거래일 평균 거래량을 계산하는 전체 과정을 단계별로 안내합니다.

requests를 사용하여 JSON을 가져오고 pandas을 사용하여 이를 데이터프레임(DataFrame)으로 변환하면, 대부분의 사용자가 이미 보유한 두 가지 라이브러리와 단 한 번의 HTTP 요청만으로 파이썬(Python)에서 무료 주식 시장 데이터 API를 호출할 수 있습니다. API 키는 필요하지 않습니다. 이 가이드는 초기 상태의 우분투(Ubuntu) 컨테이너에서 시작하여 끝까지 진행되며, 마지막에는 단일 티커(ticker)에 대한 20거래일 평균 거래량을 출력합니다. 각 엔드포인트가 제공하는 데이터는 무료 주식 시장 데이터 API 가이드에서 다루고 있으며, 이 페이지는 해당 API의 파이썬 구현 측면을 설명합니다.

신규 Ubuntu 컨테이너에서 Python, requests 및 pandas 설정하기

기본 Ubuntu 이미지에는 Python의 venv 모듈이 포함되어 있지 않으며, 최신 릴리스에서는 시스템 인터프리터에 패키지를 설치하는 것을 제한합니다. 가상 환경(virtual environment)을 사용하면 이 두 가지 문제를 모두 해결할 수 있으며, 현재의 모든 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()는 응답 본문을 딕셔너리(dictionary) 형태로 파싱합니다. 데이터의 형태를 간략히 나타내면 다음과 같습니다.

{
  "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으로 불러오는 방법

엄선된 쿼리는 정해진 질문에 대한 답을 제공합니다. 특정 티커(ticker)에 대한 일봉 데이터는 /api/demo/sql의 비로그인 SQL 엔드포인트에서 가져올 수 있으며, 이 엔드포인트는 sql 매개변수로 읽기 전용 쿼리를 받아 동일한 columns 및 rows 쌍을 반환합니다. 2026년 9월 기준, 이 엔드포인트의 제한 사항은 모든 응답에 명시되어 있습니다: 500행, 20초, 1년 치 이력, 키(key) 불필요. 키를 사용하는 무료 SQL API는 하루 100회 쿼리 및 더 깊은 이력 조회를 지원하며, 요청 코드는 동일합니다.

SQL 내의 두 가지 안전장치가 평균값의 신뢰성을 유지합니다. 세션 중에 업데이트되는 테이블은 당일에 대해 두 개 이상의 행을 가질 수 있으며, 시장이 열려 있는 동안 당일 거래량은 부분적으로만 집계됩니다. 쿼리는 date < today()를 사용하여 어제 날짜에서 중단되며, GROUP BY date 및 max(volume)을 통해 특정 날짜의 중복 행을 병합합니다. 일봉(OHLCV bar)이 테이프(tape) 데이터로부터 어떻게 구성되는지 확인하지 않았다면, 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의 필드 순서가 유지됩니다. 이후 두 가지 변환이 이어집니다. 문자열로 전달된 날짜는 실제 타임스탬프(timestamp)로 변환되며, volume는 응답 데이터가 텍스트로 직렬화될 경우를 대비하여 숫자로 강제 변환됩니다. 과거순으로 정렬하면 차트가 요구하는 순서대로 데이터프레임이 구성됩니다. 정확히 20개 행에 대한 df["volume"].mean()은 20거래일 이동평균이며, 마지막 줄은 해당 기간과 함께 평균값을 백만 단위로 출력합니다.

20일 평균 거래량은 실제로 무엇을 측정하는가?

평균 거래량은 변동성이 큰 일일 거래량 데이터를 평활화합니다. 단일 세션의 거래량은 지수 리밸런싱, 옵션 만기일, 실적 발표일, 주요 뉴스 등에 따라 크게 변동합니다. 20개 세션은 대략 한 달의 달력 일수에 해당하며, 이러한 급등락을 완화하기에 충분히 길면서도 특정 종목의 거래 활성도 변화를 추적하기에는 충분히 짧은 기간입니다. 아래 패널은 스크립트가 출력하는 것과 동일한 통계치를 동일한 일일 데이터 테이블에서 계산하여 최근 몇 달간의 데이터를 보여주며, 원본 세션 데이터와 이동평균선을 나란히 배치했습니다.

조회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
직접 실행해 보기

일일 데이터 시리즈는 세션당 하나의 지점으로 구성되며, 평활화된 시리즈는 해당 세션과 이전 19개 세션의 평균값입니다. 이 패널은 2026-08-17부터 2026-10-02까지의 기간을 다루며, 34개의 세션은 각각 20개 세션의 전체 윈도우를 확보하고 있습니다. 해당 기간의 마지막 세션에서 AAPL은 33.3백만 주가 거래되었으며, 20일 평균 거래량은 42.2백만 주였습니다. 이 평균값은 본 페이지가 생성된 날 스크립트가 출력한 수치입니다. 오늘 다시 실행하면 20개 세션의 기준점이 이동하므로 해당 수치도 함께 변동합니다.

티커 교체

TICKER을 변경하면 해당 스크립트는 표에 있는 모든 미국 상장 종목에 적용됩니다. 아래 비교 분석은 네 개의 주요 종목을 대상으로 동일한 20거래일 계산을 수행한 결과이며, 인덱스 ETF와 세 개의 메가캡(mega-cap, 초대형주) 종목 전반에서 '평균 거래량'이 의미하는 바를 빠르게 파악할 수 있도록 합니다.

조회주요 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
직접 실행해 보기

NVDA는 4개 종목 중 20거래일 평균 거래량이 가장 많아 일평균 110백만 주를 기록했으며, MSFT은 21.3백만 주로 가장 적었습니다. 모든 수치는 2026-09-04부터 2026-10-02까지의 거래일을 기준으로 측정되었습니다. 여기서 AAPL의 수치는 위 차트가 끝나는 지점의 값과 동일합니다. 이는 어디에 나타나든 동일한 정의로 한 번만 계산된 결과입니다.

429 오류는 어떤 모습이며, 어떻게 정중하게 재시도해야 합니까?

속도 제한(rate limit)은 데이터가 아닌 HTTP 상태 코드로 전달됩니다. 응답에는 429 Too Many Requests 상태가 포함되며, 종종 대기해야 할 초 단위를 담은 Retry-After 헤더가 함께 옵니다. 응답 본문에는 요청하신 데이터가 들어있지 않으므로, fetch 도우미(helper)는 구문 분석을 수행하기 전에 status_code를 먼저 확인합니다. 해당 규칙은 다음과 같습니다.

  1. 200: JSON을 구문 분석하고 반환합니다.
  2. 429 또는 기타 5xx: Retry-After가 존재하면 해당 시간만큼 대기하고, 그렇지 않으면 지수적 백오프(2, 4, 8, 16초 순으로 대기)를 수행한 뒤 총 네 번까지 재시도합니다.
  3. 기타 4xx: 중단합니다. 응답 본문에는 쿼리가 거부된 정확한 이유가 명시됩니다. 존재하지 않는 테이블이나 읽기 전용 게이트에서 거부된 문장 등은 아무리 재시도해도 결과가 바뀌지 않습니다. 예를 들어, 알 수 없는 테이블을 요청하면 400 오류가 반환됩니다.

두 가지 습관을 지키면 제한을 피할 수 있습니다. 큐레이션된 결과는 최대 10분마다 갱신되므로, 그보다 빠르게 반복 호출(polling)하는 것은 동일한 바이트를 가져오는 낭비일 뿐이며 로컬에 페이로드를 캐싱하는 것은 비용이 들지 않습니다. 또한 필요한 데이터만 요청하십시오. 1년 치 데이터를 가져와 버리는 대신, 20개 세션 평균을 위해 LIMIT 20를 사용하십시오. 네트워크 오류(연결 끊김, DNS 문제 등)는 requests.RequestException으로 나타나며, 이는 도우미가 포착하지 못합니다. 스크립트가 무인으로 실행되는 경우 호출부를 try/except로 감싸야 합니다.

자주 묻는 질문(FAQ)

파이썬용 무료 주식 시장 데이터 API가 있습니까?

있습니다. 이 가이드의 데모 엔드포인트는 requests에서 인증되지 않은 GET 요청을 처리하며, pandas로 바로 불러올 수 있는 JSON 형식을 제공합니다. /api/demo?q=<key>의 큐레이션된 쿼리와 /api/demo/sql의 수동 작성 읽기 전용 SQL을 사용할 수 있으며, 500개 행과 1년 치 데이터로 제한됩니다. 키나 가입 절차는 필요하지 않습니다.

파이썬에서 주식 데이터를 가져오려면 API 키가 필요합니까?

여기서 사용하는 데모 계층에는 필요하지 않습니다. 무료 키를 사용하면 하루 100회 쿼리로 한도가 상향되고 더 긴 과거 데이터를 조회할 수 있으며, 파이썬 코드는 변경되지 않습니다. 동일한 엔드포인트를 LLM용 도구로 활용하려는 개발자는 AI 에이전트를 위한 시장 데이터 활용 능력부터 시작할 수 있습니다.

JSON API 응답을 pandas DataFrame으로 어떻게 변환합니까?

resp.json()로 본문을 파싱한 후, 행 객체 리스트를 pd.DataFrame(rows, columns=columns)에 전달합니다. JSON에는 날짜 형식이 없고 일부 API는 십진수를 텍스트로 직렬화하므로, 산술 연산을 수행하기 전에 pd.to_datetime으로 날짜 문자열을 변환하고 pd.to_numeric로 숫자 문자열을 변환해야 합니다.

주식 API 호출 시 429 오류는 무엇을 의미합니까?

HTTP 429는 'Too Many Requests(요청 과다)'를 의미하며, 서버가 호출자의 요청 속도를 제한하고 있음을 뜻합니다. Retry-After 헤더가 제공되면 해당 헤더에 명시된 초만큼 대기하고, 그렇지 않으면 지수적으로 대기 시간을 늘려야 합니다. 10분마다 갱신되는 결과를 반복 조회하기보다는 응답을 캐싱하는 것이 좋습니다.

pandas에서 평균 거래량을 어떻게 계산합니까?

volume 컬럼을 사용하여 세션당 한 행을 로드하고, 마지막 20개의 완전한 세션을 유지한 뒤 df["volume"].mean()를 호출합니다. 더 긴 기간에 걸쳐 롤링 평균을 구하려면 위 트레이스 패널에서 사용하는 df["volume"].rolling(20).mean()를 사용하십시오.


위의 모든 패널은 하단에 정확한 SQL을 포함하고 있으며, 스크립트 역시 앞에 requests 호출이 붙는 동일한 개념입니다. 무료 API 키를 제공합니다. 하루 100회 쿼리, 22년 치 데이터, 원시 테이프(raw tape)에 대한 SQL 조회가 가능합니다. 카드 등록은 필요 없습니다. API 키 받기

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