AlgoLab Blog · KIS API 실무 · 2026

KIS 주식기본조회 CTPF1002R — 봇이 쓸 필드 9개

KIS API · 종목정보 2026-10-04 · 약 7분 읽기 · 알고랩 AlgoLab
한 줄 요약

한국투자증권 KIS API의 주식기본조회는 GET /uapi/domestic-stock/v1/quotations/search-stock-info에 tr_id CTPF1002R, 쿼리 PRDT_TYPE_CD=300·PDNO=005930 두 개만 넣으면 종목 1개의 '기본 정보' 67개 필드를 돌려줍니다. 봇에 쓸 것은 거래정지(tr_stop_yn)·관리종목(admn_item_yn)·NXT 거래가능(cptt_trad_tr_psbl_yn)·상장일·상장주수 등 9개입니다. 공식 저장소 legacy/README.md의 모의투자 제공 칸이 비어 있어 실전 앱키로만 부를 수 있고, 공식 kis_auth.py는 모의 모드에서 C로 시작하는 tr_id를 VTPF1002R로 바꿔 보내므로 모의 키로는 실패합니다.

목차

  1. 스펙 한 장 — tr_id·URL·모의 지원
  2. 요청·응답 실물
  3. 67개 중 봇이 쓸 필드 9개
  4. 함정 5가지
  5. 현재가 플래그·마스터파일과 어떻게 나눠 쓰나
  6. 자주 묻는 질문

현재가 조회 한 번으로 주문 전 안전장치를 만드는 법은 허브 글 KIS API 현재가 조회 — 관리종목·VI까지 한 번에에서, 관리종목·투자경고를 거르는 플래그는 mang_issu_cls_code 관리종목 필터에서 다뤘습니다. 이 글은 그 둘과 짝을 이루면서 사이트에서 한 번도 다루지 않았던 주식기본조회(CTPF1002R) 하나만 떼어 냅니다. 출처는 한국투자증권 공식 GitHub 저장소 koreainvestment/open-trading-api의 examples_llm/domestic_stock/search_stock_info/, MCP/KIS Code Assistant MCP/data.csv, MCP/Kis Trading MCP/configs/domestic_stock.json, legacy/README.md·legacy/postman/입니다(2026-10-04 zipball 기준).

1. 스펙 한 장 — tr_id·URL·모의 지원

항목값출처
API 이름주식기본조회 [v1_국내주식-067]search_stock_info.py
메서드·URLGET /uapi/domestic-stock/v1/quotations/search-stock-info같은 파일 API_URL
tr_idCTPF1002R (실전)같은 파일 · Postman J_주식기본조회
PRDT_TYPE_CD300 주식·ETF·ETN·ELW / 301 선물옵션 / 302 채권 / 306 ELSdata.csv args
PDNO종목번호 6자리, ETN은 Q로 시작(예: Q500001)data.csv args
모의투자미제공 (제공 여부 칸 공란 · 실전 Postman 컬렉션에만 존재)legacy/README.md · legacy/postman/README.md
응답output 단일 객체, 필드 67개chk_search_stock_info.py COLUMN_MAPPING

2. 요청·응답 실물

헤더는 다른 KIS 시세 API와 같습니다(authorization·appkey·appsecret·tr_id·custtype). 공식 Postman 샘플의 요청 줄은 아래와 같습니다.

GET {{PROD}}/uapi/domestic-stock/v1/quotations/search-stock-info?PDNO=005930&PRDT_TYPE_CD=300
content-type: application/json
authorization: Bearer {{PROD_TOKEN}}
appkey: {{PROD_APPKEY}}
appsecret: {{PROD_APPSECRET}}
tr_id: CTPF1002R
custtype: P

파이썬으로는 이렇게 부릅니다. 앱키는 환경 변수로만 읽고 코드에 적지 않습니다.

import os, requests

BASE = "https://openapi.koreainvestment.com:9443"   # 실전 도메인 — 모의 도메인에선 이 API가 없다
URL  = BASE + "/uapi/domestic-stock/v1/quotations/search-stock-info"

def stock_info(code, token):
    headers = {
        "content-type": "application/json",
        "authorization": f"Bearer {token}",
        "appkey": os.environ["KIS_APPKEY"],
        "appsecret": os.environ["KIS_APPSECRET"],
        "tr_id": "CTPF1002R",
        "custtype": "P",
    }
    r = requests.get(URL, headers=headers,
                     params={"PRDT_TYPE_CD": "300", "PDNO": code}, timeout=5)
    body = r.json()
    if body.get("rt_cd") != "0":
        raise RuntimeError(f"{body.get('msg_cd')} {body.get('msg1')}")
    return body["output"]          # dict 하나 — 리스트가 아니다

응답 output의 키는 공식 매핑 기준으로 아래처럼 생겼습니다(값은 종목마다 다르므로 생략, 67개 중 일부).

{
  "rt_cd": "0", "msg_cd": "...", "msg1": "...",
  "output": {
    "pdno": "...", "prdt_abrv_name": "...", "mket_id_cd": "...", "excg_dvsn_cd": "...",
    "lstg_stqt": "...", "kospi200_item_yn": "...",
    "scts_mket_lstg_dt": "...", "kosdaq_mket_lstg_dt": "...", "lstg_abol_dt": "...",
    "etf_dvsn_cd": "...", "etf_chas_erng_rt_dbnb": "...", "etf_etn_ivst_heed_item_yn": "...",
    "tr_stop_yn": "...", "admn_item_yn": "...",
    "std_idst_clsf_cd_name": "...", "idx_bztp_lcls_cd_name": "...",
    "cptt_trad_tr_psbl_yn": "...", "nxt_tr_stop_yn": "...",
    "...": "..."
  }
}

3. 67개 중 봇이 쓸 필드 9개

67개 필드의 상당수(ocr_no·frbd_mket_lstg_dt 프리보드 상장일·mfnd_opng_dt 뮤추얼펀드 개시일 등)는 자동매매와 무관합니다. 실제로 쓸모 있는 것만 추리면 아래 9개입니다. 한글 뜻은 공식 COLUMN_MAPPING 원문입니다.

필드공식 뜻봇에서 쓰는 곳
tr_stop_yn거래정지여부주문 전 차단
admn_item_yn관리종목여부유니버스 제외
etf_etn_ivst_heed_item_ynETFETN투자유의종목여부ETF·ETN 전략의 제외 필터
cptt_trad_tr_psbl_ynNXT 거래종목여부프리·애프터마켓 주문 라우팅 판단
nxt_tr_stop_ynNXT 거래정지여부NXT로 보낼 주문 차단
lstg_stqt상장주수시가총액 직접 계산·유동성 필터
scts_mket_lstg_dt / kosdaq_mket_lstg_dt유가증권/코스닥시장 상장일자신규 상장 N일 미만 제외
kospi200_item_yn코스피200종목여부대형주 유니버스
etf_chas_erng_rt_dbnbETF추적수익율배수레버리지·인버스 ETF 구분

업종 분산이 필요하면 std_idst_clsf_cd_name(표준산업분류코드명)이나 idx_bztp_lcls_cd_name(지수업종대분류코드명)으로 한 업종 쏠림을 막을 수 있습니다. 레버리지 ETF를 다룰 때의 별도 주의점은 ETF 자동매매 — 괴리율·유동성에 있습니다.

① 마스터파일 kospi_code.mst 전 종목 · 하루 1번 ② CTPF1002R 후보만 · 장 전 1번 상장일·NXT·배수 ③ FHKST01010100 주문 직전 · 매번 temp_stop_yn 등 →→ 넓게 → 좁게 → 직전에: 호출 횟수를 줄이는 3단 필터 ②는 하루 한 번 캐시, ③만 주문마다 호출
주식기본조회는 '장 전 1회' 칸에 둔다 — 5장에서 이유를 설명.

4. 함정 5가지

함정 1 — 모의 앱키로 부르면 tr_id가 바뀐다

공식 examples_user/kis_auth.py의 _url_fetch()는 tr_id 첫 글자가 T·J·C이고 모의 모드이면 "V" + ptr_id[1:]로 바꿉니다. CTPF1002R은 C로 시작하므로 모의 키로는 VTPF1002R이 나가는데, 이 API는 모의 제공 목록에 없습니다. 반대로 FHKST01010100(현재가)은 F로 시작해 모의·실전이 같습니다. 그래서 '현재가는 모의에서 되는데 기본조회만 안 된다'는 현상이 나옵니다. 모의 환경에서 전략을 검증할 때 종목정보는 실전 앱키(조회 전용)로 받고 주문만 모의로 보내는 식으로 키를 나누는 것이 일반적입니다 — KIS 모의투자 한계 — 안 되는 API 10가지 참고.

함정 2 — 이름이 비슷한 '상품기본조회' CTPF1604R

같은 종목정보 묶음에 상품기본조회가 따로 있습니다. tr_id CTPF1604R, URL은 /quotations/search-info(뒤에 stock-가 없음)이고, Postman 설명상 PRDT_TYPE_CD에 512(미국 나스닥)·515(일본) 같은 해외 상품 코드까지 받는 범용 조회입니다. 국내주식의 거래정지·관리종목·NXT 필드가 필요하면 search-stock-info(CTPF1002R) 쪽입니다. URL 한 단어 차이라 복사하다 섞이기 쉽습니다.

함정 3 — Postman 헤더 설명에 다른 tr_id가 적혀 있다

공식 실전계좌_POSTMAN_샘플코드_v2.6.json의 J_주식기본조회 요청을 보면 tr_id 헤더의 값은 CTPF1002R인데 설명란에는 [실전투자] CTOS5011R이 적혀 있습니다. 다른 API 설명이 복사된 것으로 보입니다. 값과 examples_llm 예제가 모두 CTPF1002R이므로 그쪽을 따르되, 의심되면 KIS Developers 포털의 원문 명세를 확인하십시오.

함정 4 — 종목 1개 = 1콜, 전 종목에 돌리면 느리다

이 API는 종목 코드 하나를 받아 한 행을 돌려줍니다. 공식 예제는 tr_cont == "M"일 때 다음 페이지를 재귀 호출(max_depth=10)하도록 짜여 있지만, 단일 종목 조회라 실질적으로 한 번에 끝납니다. 문제는 코스피·코스닥 2천여 종목 전체에 돌릴 때입니다. 공식 kis_auth.py의 실전 대기값 0.05초에 응답 시간까지 더하면 수 분이 걸리고, 같은 앱키로 다른 봇이 돌고 있으면 EGW00201(초당 거래건수 초과)을 만나기 쉽습니다(EGW00201 해결). 전 종목 1차 필터는 종목코드 마스터파일(kospi_code.mst)로 하고, 이 API는 후보 수십 개에만 쓰는 것이 맞습니다.

함정 5 — 'Y/N' 문자열과 빈 값, 그리고 갱신 시점

KIS 응답은 숫자도 문자열로 옵니다. lstg_stqt는 int()로, 상장일은 YYYYMMDD 문자열로 보고 변환해야 하며, 코스피 종목은 kosdaq_mket_lstg_dt가, 코스닥 종목은 scts_mket_lstg_dt가 쓸모없는 값이므로 mket_id_cd로 어느 쪽을 읽을지 먼저 정합니다. 더 중요한 것은 공식 문서에 이 필드들이 장중 언제 갱신되는지 적혀 있지 않다는 점입니다. 장중에 걸리는 거래정지·VI는 이 값보다 현재가 응답의 temp_stop_yn·iscd_stat_cls_code로 직전에 다시 확인하십시오.

from datetime import date, datetime

def pass_filter(o, min_days=60):
    if o["tr_stop_yn"] == "Y" or o["admn_item_yn"] == "Y":
        return False, "거래정지/관리종목"
    if o.get("etf_etn_ivst_heed_item_yn") == "Y":
        return False, "ETF·ETN 투자유의"
    lstg = o["kosdaq_mket_lstg_dt"] or o["scts_mket_lstg_dt"]   # 시장별로 한쪽만 의미 있음
    if lstg and (date.today() - datetime.strptime(lstg, "%Y%m%d").date()).days < min_days:
        return False, f"상장 {min_days}일 미만"
    return True, "ok"

def nxt_ok(o):
    # 프리·애프터마켓(NXT)에 주문을 보내기 전 확인
    return o.get("cptt_trad_tr_psbl_yn") == "Y" and o.get("nxt_tr_stop_yn") != "Y"

위 코드의 or 분기는 해당 시장이 아닐 때 빈 문자열이 온다는 가정입니다. 실제 형식은 첫 호출에서 print(o)로 확인한 뒤 확정하십시오.

5. 현재가 플래그·마스터파일과 어떻게 나눠 쓰나

경로범위강점언제
마스터파일 kospi_code.mst전 종목API 호출 0회하루 1번
주식기본조회 CTPF1002R종목 1개상장일·상장주수·NXT 거래가능·ETF 배수장 전 후보만, 캐시
현재가 FHKST01010100종목 1개temp_stop_yn·mang_issu_cls_code 등 장중 상태주문 직전 매번

NXT 거래가능 여부는 이 API에서만 한 필드로 바로 나오는 정보라, NXT·KRX 주문 라우팅에서 SOR·NXT로 주문을 보내는 봇이라면 장 전 캐시에 꼭 넣어 두는 것이 좋습니다. 시가총액을 직접 계산할 때는 lstg_stqt × stck_prpr로 시가총액 상위 API의 stck_avls를 대조할 수 있습니다. 에러 코드 전반은 KIS API 에러코드 정리에 있습니다.

이 글의 필터는 종목 추천이 아니라 '사면 안 되는 종목을 거르는 안전장치'입니다. 필터를 통과했다고 수익이 나는 것은 아니며, 과거 성과가 미래를 보장하지 않습니다. KIS API 명세는 예고 없이 바뀔 수 있으니 KIS Developers 공식 문서를 확인하십시오.

6. 자주 묻는 질문

KIS API 주식기본조회의 tr_id와 URL은 무엇인가요?

tr_id는 CTPF1002R이고 GET /uapi/domestic-stock/v1/quotations/search-stock-info 로 호출합니다. 쿼리 파라미터는 PRDT_TYPE_CD(주식·ETF·ETN·ELW는 300)와 PDNO(종목번호 6자리, ETN은 Q로 시작) 두 개입니다.

모의투자 앱키로 주식기본조회를 호출할 수 있나요?

공식 저장소 legacy/README.md의 모의투자 제공 여부 칸이 비어 있고 Postman 샘플도 실전 컬렉션에만 있습니다. 공식 kis_auth.py는 모의 모드에서 C로 시작하는 tr_id를 VTPF1002R로 바꿔 보내므로 실패합니다. 종목정보는 실전 앱키로 조회하고 주문만 모의로 보내는 식으로 키를 나눠 쓰는 것이 일반적입니다.

주식기본조회와 상품기본조회(CTPF1604R)는 무엇이 다른가요?

상품기본조회는 /quotations/search-info 경로에 tr_id CTPF1604R을 쓰며 해외 상품 코드(512 미국 나스닥 등)까지 받는 범용 조회입니다. 국내주식의 거래정지여부·관리종목여부·NXT 거래종목여부 같은 필드가 필요하면 search-stock-info(CTPF1002R)를 써야 합니다.

주식기본조회만으로 장중 거래정지를 판단해도 되나요?

권하지 않습니다. 공식 문서에 이 필드들의 장중 갱신 시점이 적혀 있지 않습니다. 주식기본조회는 장 전 후보 필터로 쓰고, 주문 직전에는 현재가 조회(FHKST01010100) 응답의 temp_stop_yn·iscd_stat_cls_code로 다시 확인하는 2단 구조가 안전합니다.

종목 필터부터 주문까지 도는 봇, 만들어 드립니다

거를 조건과 전략만 알려 주시면 됩니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기