본문 바로가기
인천고래 프로그램

KRX 시세 데이터, 이제 로그인 & API를 신청 해야 쓸 수 있네요. ㅜ

by 인천고래
반응형

안녕하세요. 주식 관련 데이터를 분석하는 투자자 인천고래입니다.

오늘 이렇게 갑자기 글을 쓰는 이유는 제가 만든 프로그램들이 에러가 나기 시작해서입니다.

 

저는 전체 종목에 대한 정보와 데이터를 한 번에 가져올 수 있도록  KRX 사이트를 이용했었는데요.

누구나 쉽게 사용할 수 있어서 시세/종목 데이터를 가져올 때 data.krx.co.kr 페이지를 직접 호출(또는 크롤링) 해서 해결되는 경우가 많았습니다.
그런데 어제부터 데이터를 호출하는데 에러(Logout)가 발생되어서 무엇이 문제일까 고민을 하면서

한참을 코딩쪽 문제일 줄 알고 해결하느라 시간을 잡아 먹었었습니다. ㅜ

인천고래 텔레그램

그래서 결국 전 종목에 대한 리스트를 못 가져와서 공지를 올리고 잠시 퍼지 상태로 있는 상태였습니다. ㅜ

(키움으로 가져올수도 있었지만 한 번에 가져오기 위해 KRX를 고집하고 있습니다.)

 

확인을 해 보니 KRX가 로그인이 필요한 구조로 바뀌면서,

이전 방식대로 호출하면 정상 응답이 아니라 logout / 접근 차단 / 빈 데이터 같은 형태로 막히는 일이 생기더라고요.

 

그래서 결론적으로 지금은 KRX 관련 오류는 KRX OPEN API(인증키 기반)를 써야 한다는 점입니다.

물론 자동로그인 형태로 해서 크롤링하는 방법도 있겠지만

OpenAPI를 사용하는 방법이 가장 안전하고, 앞으로도 유지될 가능성이 큰 방법입니다.

KRX API 신청

 

다만… 여기서 제가 한 번 “완벽하게 삽질(?)”을 했습니다.

API만 신청해서 발급만 받으면 될 줄 알았는데
인증키를 발급받았는데도 계속 401 Unauthorized API Call 이 떠서, 한참을 코드 문제로 착각했거든요.

C:\Users\SH_PER2\anaconda3\envs\py37_32\python.exe "C:/Users/SH_PER2/PycharmProjects/bot/_App/Trader/bot_V7_Anlysis/KRX API 활용.py"
r <Response [401]>
Traceback (most recent call last):
  File "C:/Users/SH_PER2/PycharmProjects/bot/_App/Trader/bot_V7_Anlysis/KRX API 활용.py", line 113, in <module>
    df_base = fetch_base_info(AUTH_KEY, bas_dd, market="KOSPI")
  File "C:/Users/SH_PER2/PycharmProjects/bot/_App/Trader/bot_V7_Anlysis/KRX API 활용.py", line 95, in fetch_base_info
    data = _krx_post(auth_key, endpoint, {"basDd": bas_dd})
  File "C:/Users/SH_PER2/PycharmProjects/bot/_App/Trader/bot_V7_Anlysis/KRX API 활용.py", line 34, in _krx_post
    raise RuntimeError(f"KRX API Error: {r.status_code} / {r.text}")
RuntimeError: KRX API Error: 401 / {"respMsg":"Unauthorized API Call","respCode":"401"}

Process finished with exit code 1

 

1) 제가 겪은 문제: 인증키를 넣었는데도 401이 계속 뜸

처음엔 “인증키가 틀렸나?” 싶어서 몇 번을 확인했는데도 계속 아래처럼 나왔습니다.

  • 401 / {"respMsg":"Unauthorized API Call","respCode":"401"}

대부분 이걸 보면 개발자는 이렇게 생각합니다.

“내가 헤더를 잘못 넣었나?”
“GET/POST를 바꿔야 하나?”
“파라미터가 basDd가 아니라 다른 건가?”

저도 정확히 그 루트로 갔습니다.


 

2) 원인: “OpenAPI 인증키 발급”만으로 끝이 아니었다

결론부터 말하면, 제 401은 코드 문제가 아니라 권한 문제였어요.

KRX OPEN API는 단계가 2개입니다.

2-1. API 인증키 발급

  • 마이페이지에서 “API 인증키 발급내역”에 키가 생성됩니다.

여기까지만 하면 “준비 완료”처럼 보이는데…

 

2-2. 사용하려는 데이터(서비스)별로 “API 이용신청”을 별도로 해야 함

이게 핵심입니다.

KRX 시리즈 일별시세정보 API 신청 (이용 기간, 목적 선택 필수)

 

즉, 위의 이미지에서 알 수 있듯이

API 인증키를 발급 받은 것과는 별도로 각 컨텐츠별로 API 이용 신청을 해야합니다.

 각 서비스(컨텐츠)는 아래와 같습니다.

https://openapi.krx.co.kr/contents/OPP/INFO/service/OPPINFO004.cmd

 

서비스 목록 - KRX | Data Marketplace OPEN API

서비스 목록 KRX OPEN API는 사용자의 편의를 위해 API 서비스를 제공하고 있습니다.사용목적에 맞는 API를 확인하신 후 자유롭게 이용하세요. ※ 데이터 제공 대상기간 : 2010년 이후 데이터 구분 API

openapi.krx.co.kr

KRX API 서비스 목록

 

이 중에서 제가 이용 신청한 정보는 아래와 같습니다.

  • “유가증권 일별매매정보”
  • “코스닥 일별매매정보”
  • “유가증권 종목기본정보”
  • “코스닥 종목기본정보”
  • “KOSPI 시리즈 일별시세정보”
  • “KOSDAQ 시리즈 일별시세정보”

API 컨텐츠별 이용현황

 

이처럼 내가 필요한 서비스를 확인하고 하나씩 들어가서 API 이용신청을 눌러서 신청을 완료하시면 됩니다.

 

예를 들어 아래처럼 "KRX 시리즈 일별시세정보" 화면으로 들어가서 맨 아래의 "API 이용신청"을 눌러서 신청을 해야 합니다.

 

신청 후에는 마이페이지 이용현황에서 상태가 보이는데요,
여기에 **“승인대기”**로 떠 있는 동안은 호출이 계속 401이 납니다.

 

즉, 401은 “승인 안 났다”, “신청하지 않았다” 의 신호일 가능성이 매우 큽니다.


 

3) 해결 방법: 승인 상태 확인 → 승인 완료 후 호출

제가 한 해결 순서를 정리해 드리자면 아래와 같습니다.

  1. 서비스 목록에서 필요한 API를 찾는다
  2. 각 API 페이지에서 [API 이용신청] 클릭
    • 기간 선택(예: 1M / 3M / 6M / 12M)
    • 신청 목적 선택(예: 개인연구)
  3. 마이페이지 → 이용현황에서 상태 확인
    • “승인대기” → 아직 호출 불가
    • “승인” → 호출 가능

그리고 승인이 완료되면,
이전에 나던 401이 “거짓말처럼” 사라집니다.


 

4) 승인 후 잘 동작한 파이썬 코드 예시 (KOSPI/KOSDAQ)

아래 코드는 KRX OPEN API에서 KOSPI/KOSDAQ 기준

  • 종목기본정보
  • 일별매매정보
    를 가져오는 형태입니다.

그리고 개발 가이드 문서를 첨부하니(KRX 사이트에서도 받을 수 있습니다.) 참고 바랍니다.

유가증권 일병매매정보 개발명세서.docx
0.01MB
유가증권 종목기본정보 개발명세서.docx
0.01MB
코스닥 일별매매정보 개발명세서.docx
0.01MB
코스닥 종목기본정보 개발명세서.docx
0.01MB

참고: 저는 위의 개발명세서를 참고하여
Python 3.7(32bit) 환경에서 테스트했습니다.
requests/pandas만 있으면 됩니다.

import requests
import pandas as pd

API_BASE = "https://data-dbg.krx.co.kr/svc/apis/sto"

# ✅ KOSPI/KOSDAQ만 (코넥스 제외)
ENDPOINTS = {
    "KOSPI": {
        "daily_trade": "/stk_bydd_trd",        # 유가증권 일별매매정보
        "base_info":  "/stk_isu_base_info",    # 유가증권 종목기본정보
    },
    "KOSDAQ": {
        "daily_trade": "/ksq_bydd_trd",        # 코스닥 일별매매정보
        "base_info":  "/ksq_isu_base_info",    # 코스닥 종목기본정보
    }
}

def _krx_post(auth_key: str, endpoint: str, payload: dict, timeout: int = 30) -> dict:
    url = API_BASE + endpoint
    headers = {
        "AUTH_KEY": auth_key.strip(),          # ✅ 공백/개행 제거 습관
        "Content-Type": "application/json",
        "Accept": "application/json",
    }
    r = requests.post(url, headers=headers, json=payload, timeout=timeout)

    if r.status_code != 200:
        raise RuntimeError(f"KRX API Error: {r.status_code} / {r.text}")

    return r.json()

def fetch_daily_trade(auth_key: str, bas_dd: str, market: str = "KOSPI") -> pd.DataFrame:
    market = market.upper()
    endpoint = ENDPOINTS[market]["daily_trade"]
    data = _krx_post(auth_key, endpoint, {"basDd": bas_dd})
    rows = data.get("OutBlock_1", [])
    df = pd.DataFrame(rows)

    # 숫자형 변환(콤마 제거)
    num_cols = [
        "TDD_CLSPRC", "CMPPREVDD_PRC", "FLUC_RT",
        "TDD_OPNPRC", "TDD_HGPRC", "TDD_LWPRC",
        "ACC_TRDVOL", "ACC_TRDVAL", "MKTCAP", "LIST_SHRS"
    ]
    for c in num_cols:
        if c in df.columns:
            df[c] = pd.to_numeric(df[c].astype(str).str.replace(",", ""), errors="coerce")

    rename_map = {
        "BAS_DD": "Date",
        "ISU_CD": "Code",
        "ISU_NM": "Name",
        "TDD_OPNPRC": "Open",
        "TDD_HGPRC": "High",
        "TDD_LWPRC": "Low",
        "TDD_CLSPRC": "Close",
        "ACC_TRDVOL": "Volume",
        "ACC_TRDVAL": "Value",
        "MKTCAP": "MarketCap",
        "LIST_SHRS": "ListShares",
    }
    df.rename(columns={k: v for k, v in rename_map.items() if k in df.columns}, inplace=True)
    df["Market"] = market
    return df

def fetch_base_info(auth_key: str, bas_dd: str, market: str = "KOSPI") -> pd.DataFrame:
    market = market.upper()
    endpoint = ENDPOINTS[market]["base_info"]
    data = _krx_post(auth_key, endpoint, {"basDd": bas_dd})
    rows = data.get("OutBlock_1", [])
    df = pd.DataFrame(rows)
    df["Market"] = market
    return df


if __name__ == "__main__":
    AUTH_KEY = "여기에_발급받은_인증키"
    bas_dd = "20260106"

    # 종목기본정보
    df_base = fetch_base_info(AUTH_KEY, bas_dd, market="KOSPI")
    print("BASE rows:", len(df_base))
    print(df_base.head())

    # 일별매매정보(OHLCV 등)
    df_trd = fetch_daily_trade(AUTH_KEY, bas_dd, market="KOSPI")
    print("TRD rows:", len(df_trd))
    print(df_trd[["Date", "Code", "Name", "Open", "High", "Low", "Close", "Volume"]].head())
 

 

5) 401이 다시 뜰 때, 체크리스트 3개만 기억하세요

승인 이후에도 401이 뜬다면 대부분 아래 중 하나입니다.

  1. API 이용현황이 아직 “승인대기”
    → 승인 완료까지 기다려야 함 (가장 흔함)
  2. AUTH_KEY 복사 시 공백/개행 포함
    → auth_key.strip() 적용
  3. 내가 신청한 API와 다른 API를 호출함
    → 예: “지수 시리즈”를 신청했는데 “주식 일별매매정보”를 호출하는 경우
    (서비스 단위로 권한이 갈립니다)

 

6) 마무리: “401은 내 코드 탓”이 아닐 수도 있습니다

저처럼 인증키 발급까지만 해놓고 “왜 안 되지?” 하면서 시간을 날리는 분들이 진짜 많을 것 같아요.
KRX OPEN API는 구조상:

  • 키 발급
  • 서비스별 이용신청(승인)

이 두 단계가 모두 끝나야 정상 호출됩니다.

저는 이걸 모르고 한참을 헤맸다가,
이용현황에서 “승인대기”를 보고 나서야 퍼즐이 맞춰졌고요.

 

오늘 밤은 편히 잘 수 있을 것 같습니다.

오늘 하루도 수고 많으셨습니다. ^^

반응형