키움 ka10042 순매수거래원순위 — 수량이 없다
ka10042(순매수거래원순위요청)는 종목 1개를 넣으면 순매수 상위 회원사의 "순위·회원사코드·회원사명" 세 칸만 돌려줍니다. 순매수 수량은 응답에 없습니다. 경로는 /api/dostk/rkinfo, 필수 body는 stk_cd·qry_dt_tp·pot_tp·sort_base 4개입니다. 수량이 필요하면 같은 경로의 ka10038(종목별증권사순위요청)을 같이 불러야 하는데, ka10038은 거꾸로 수량은 있고 회원사코드가 없고, 기간 dt도 5일을 "5"가 아니라 "4"로 받습니다. 응답 mmcm_cd는 "6"처럼 앞자리 0이 없어 ka10039·ka10043에 넘기려면 zfill(3)이 필요합니다. (공식 명세 kiwoom_api_spec.json·예제, main 953e5db 2026-09-15 기준, 2026-10-23 확인)
목차
- ka10042는 어디에 쓰나 — 거래원 TR 지도
- 요청·응답 실물
- 함정 6가지
- ka10042 + ka10038을 묶는 코드
- 자주 묻는 질문
1. ka10042는 어디에 쓰나 — 거래원 TR 지도
"이 종목을 최근 5일간 어느 창구가 가장 많이 순매수했나"를 한 번에 보는 TR입니다. 키움 REST API의 거래원(회원사) 계열은 두 경로로 흩어져 있어서, 무엇을 넣고 무엇을 받는지부터 정리해 두면 헷갈리지 않습니다.
| TR | 경로 | 넣는 것 | 받는 것 | 수량 | 회원사코드 |
|---|---|---|---|---|---|
| ka10042 순매수거래원순위 | rkinfo | 종목 1개 + 기간 | 순매수 상위 회원사 | 없음 | 있음 (mmcm_cd) |
| ka10038 종목별증권사순위 | rkinfo | 종목 1개 + 기간 | 회원사별 매수·매도·누적순매수 | 있음 | 없음 (이름만) |
| ka10039 증권사별매매상위 | rkinfo | 회원사 1곳 + 기간 | 그 창구의 순매수 상위 종목 | 있음 | 요청에 넣음 |
| ka10043 거래원매물대분석 | stkinfo | 종목 + 회원사 + 기간 | 일별 매수·매도 수량 | 있음 | 요청에 넣음 |
| ka10102 회원사 리스트 | stkinfo | 없음 | 코드·이름 목록 | — | 3자리 ("001") |
명세 337개 TR 중 응답에 mmcm_cd가 들어 있는 것은 ka10042 하나뿐입니다(ka10102는 필드명이 code). 그래서 ka10042의 실제 쓰임새는 "순위표"라기보다 종목에서 회원사코드를 뽑아내는 입구에 가깝습니다. 뽑은 코드를 ka10043(거래원매물대분석)이나 ka10052(거래원순간거래량)에 넘겨 일별·순간 수량을 받는 순서입니다. 당일 상위 5개 창구만 필요하면 ka10040(당일주요거래원)이 더 간단합니다.
2. 요청·응답 실물
명세의 request_example 원문입니다. 공식 예제 examples/국내주식/순위정보/get_domestic_net_buy_broker_rank.py의 docstring도 같은 값을 씁니다.
POST https://api.kiwoom.com/api/dostk/rkinfo
api-id: ka10042
authorization: Bearer {접근토큰}
{
"stk_cd": "005930",
"strt_dt": "20241031",
"end_dt": "20241107",
"qry_dt_tp": "0",
"pot_tp": "0",
"dt": "5",
"sort_base": "1"
}
요청 파라미터 설명 원문입니다.
| element | 필수 | 명세 설명 |
|---|---|---|
stk_cd | Y | 거래소별 종목코드 (KRX:039490, NXT:039490_NX, SOR:039490_AL) · 길이 6 |
strt_dt / end_dt | N | YYYYMMDD |
qry_dt_tp | Y | 0:기간으로 조회, 1:시작일자, 종료일자로 조회 |
pot_tp | Y | 0:당일, 1:전일 |
dt | N | 5:5일, 10:10일, 20:20일, 40:40일, 60:60일, 120:120일 |
sort_base | Y | 1:종가순, 2:날짜순 |
응답 response_example 원문(6행 중 앞 4행)입니다.
{
"netprps_trde_ori_rank": [
{ "rank": "1", "mmcm_cd": "36", "mmcm_nm": "키움증권" },
{ "rank": "2", "mmcm_cd": "50", "mmcm_nm": "키움증권" },
{ "rank": "3", "mmcm_cd": "45", "mmcm_nm": "키움증권" },
{ "rank": "4", "mmcm_cd": "6", "mmcm_nm": "키움증권" },
...
],
"return_code": 0,
"return_msg": "정상적으로 처리되었습니다"
}
응답 필드는 rank·mmcm_cd·mmcm_nm 셋이 전부이고, 공식 예제의 NUMERIC_COLUMNS도 빈 튜플 ()입니다. 숫자로 바꿀 칸이 하나도 없다는 뜻입니다.
3. 함정 6가지
① 순매수 "순위"인데 순매수 수량이 없다
1위와 2위의 차이가 10만 주인지 100주인지 응답만으로는 알 수 없습니다. "상위 3개 창구 순매수 합계가 거래량의 몇 %"같은 필터를 ka10042만으로 만들 수는 없습니다. 수량은 ka10038의 acc_netprps_qty(누적순매수수량)·buy_qty·sell_qty, 또는 회원사코드를 넣은 ka10043에서 받아야 합니다. ka10038 응답 예시의 실물입니다.
"rank_1": "+34881", "rank_2": "-13253", "rank_3": "+21628", "prid_trde_qty": "43",
"stk_sec_rank": [
{ "rank": "1", "mmcm_nm": "키움증권", "buy_qty": "+9800", "sell_qty": "-2813", "acc_netprps_qty": "+6987" },
...
ka10038은 수량 문자열에 +·- 부호가 붙어 오고, 매도수량도 "-2813"처럼 음수 부호로 옵니다. 합계를 낼 때 부호를 그대로 더하면 매도가 빠지는 계산이 되니 절댓값으로 바꿔 쓰십시오.
② ka10038과 기간 dt 번호가 하나씩 어긋난다
같은 "5일"을 두 TR이 다르게 받습니다. 명세 337개 중 dt에 일수를 받는 TR 10개를 전수로 보면 ka10038만 "일수 − 1"로 적습니다(1:전일, 4:5일, 9:10일, 19:20일, 39:40일, 59:60일, 119:120일). 명세 값으로 변환표를 뽑은 결과입니다(Python 3.10.4, 실측).
5일 -> ka10042 dt= '5' ka10038 dt='4'
20일 -> ka10042 dt= '20' ka10038 dt='19'
60일 -> ka10042 dt= '60' ka10038 dt='59'
120일 -> ka10042 dt= '120' ka10038 dt='119'
ka10042용 상수 "5"를 ka10038에 그대로 넣으면 명세 목록에 없는 값이라 오류가 나거나, 다른 기간으로 계산될 수 있습니다(동작은 명세에 없음). 두 TR을 묶는다면 기간은 일수(int)로 들고 있다가 TR마다 변환하는 편이 안전합니다. 참고로 ka10034·ka10036·ka10037은 "10;10일"처럼 쉼표 대신 세미콜론이 찍혀 있으니 명세 문자열을 파싱해 상수를 만드는 방식도 피하십시오.
③ 응답 mmcm_cd는 앞자리 0이 없다
요청에 회원사코드를 받는 ka10039·ka10043·ka10052·ka10078은 모두 mmcm_cd 길이 3, 설명 "회원사 코드는 ka10102 조회"이고 ka10102는 "001"~"012"처럼 3자리로 줍니다. 그런데 ka10042 응답은 길이 20에 "6"·"36"입니다. 예시 6행을 zfill(3)해 ka10102 예시 목록과 대조한 결과입니다(실측).
rank 1 raw= '36' zfill='036' in_ka10102_example=False name=키움증권
rank 4 raw= '6' zfill='006' in_ka10102_example=True name=키움증권
ka10102 예시는 10개만 실려 있어 대부분 False가 나오는 건 당연하지만, 한 줄은 짚어 둘 만합니다. ka10102 예시에서 "006"의 이름은 "신 영"인데 ka10042 예시는 같은 6을 "키움증권"이라고 적었습니다. 즉 ka10042 응답 예시의 회원사명 6개는 전부 자리채움 값입니다. 코드↔이름 대응은 예시가 아니라 실제 ka10102 호출 결과로 만드십시오. 서버가 2자리 코드를 3자리와 같게 받는지는 명세에 없으므로, 다른 TR에 넘길 때는 zfill(3)으로 맞추는 쪽이 안전합니다.
④ sort_base "종가순·날짜순"은 ka10043에서 복사된 설명이다
순위 TR인데 정렬 기준이 1:종가순, 2:날짜순입니다. 이 문구와 qry_dt_tp·pot_tp·dt 설명이 ka10043과 글자 하나까지 같습니다. ka10043은 일별 행을 주니 날짜순이 말이 되지만 ka10042 응답에는 날짜도 종가도 없습니다. 어느 값을 넣어도 받은 행은 rank 칸 기준으로 다시 정렬해 쓰십시오. ka10043과의 차이는 필수 여부입니다. ka10043은 strt_dt·end_dt·dt가 필수(Y)지만 ka10042는 셋 다 선택(N)입니다.
⑤ 공식 예제 함수는 2024년 날짜를 항상 보낸다
공식 예제의 함수 시그니처는 strt_dt='20241031', end_dt='20241107', dt='5'가 기본값이고, None이 아니면 body에 넣습니다. qry_dt_tp="1"(시작·종료일 조회)로 바꾸면서 날짜를 안 넘기면 2024년 10~11월 구간이 조회됩니다. 오류가 아니라 옛날 데이터가 오는 것이라 눈치채기 어렵습니다. 예제를 가져다 쓸 때는 기본값을 None으로 바꾸고 qry_dt_tp에 맞는 쪽만 보내십시오.
⑥ 순매도 순위는 없다
ka10038은 qry_tp(1:순매도순위정렬, 2:순매수순위정렬)로 방향을 고르지만 ka10042에는 이 파라미터가 없습니다. "어느 창구가 가장 많이 던졌나"는 ka10038 qry_tp "1"로 받아야 합니다.
4. ka10042 + ka10038을 묶는 코드
토큰은 환경변수에서 읽고 위 ①~⑥을 코드에서 막습니다. cont-yn·next-key 연속조회는 연속조회 글, 오류 응답은 키움 return_code 글을 참고하십시오.
import os, requests
import pandas as pd
BASE = "https://api.kiwoom.com" # 모의: https://mockapi.kiwoom.com
TOKEN = os.environ["KIWOOM_TOKEN"]
RK = "/api/dostk/rkinfo" # ka10042·ka10038 공통
DT_42 = {5: "5", 10: "10", 20: "20", 40: "40", 60: "60", 120: "120"}
DT_38 = {1: "1", 5: "4", 10: "9", 20: "19", 40: "39", 60: "59", 120: "119"} # ② 하나씩 어긋남
def post(api_id, body):
h = {"api-id": api_id, "authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json;charset=UTF-8"}
r = requests.post(BASE + RK, headers=h, json=body, timeout=10)
j = r.json()
if j.get("return_code") not in (0, None):
raise RuntimeError(f'{api_id} {j.get("return_code")} {j.get("return_msg")}')
return j
def num(s): # "+6987"·"-2813" → 6987.0 / -2813.0
s = str(s).strip()
return float(s) if s not in ("", "None") else float("nan")
def net_buy_brokers(stk_cd, days=5):
j42 = post("ka10042", {"stk_cd": stk_cd, "qry_dt_tp": "0", "pot_tp": "0",
"dt": DT_42[days], "sort_base": "1"}) # ⑤ 날짜는 안 보냄
a = pd.DataFrame(j42.get("netprps_trde_ori_rank", []))
a["rank"] = a["rank"].astype(int)
a = a.sort_values("rank") # ④ rank로 재정렬
a["mmcm_cd"] = a["mmcm_cd"].str.strip().str.zfill(3) # ③ 3자리로
j38 = post("ka10038", {"stk_cd": stk_cd, "dt": DT_38[days], "qry_tp": "2"})
b = pd.DataFrame(j38.get("stk_sec_rank", []))
for c in ("buy_qty", "sell_qty", "acc_netprps_qty"):
b[c] = b[c].map(num)
b["sell_qty"] = b["sell_qty"].abs() # ① 매도 부호 제거
key = lambda s: s.str.replace(" ", "", regex=False) # "교 보" → "교보"
a["k"], b["k"] = key(a["mmcm_nm"]), key(b["mmcm_nm"])
out = a.merge(b[["k", "buy_qty", "sell_qty", "acc_netprps_qty"]], on="k", how="left")
return out.drop(columns="k")
if __name__ == "__main__":
print(net_buy_brokers("005930", days=5).head())
병합 후 acc_netprps_qty가 NaN인 행은 이름이 안 맞은 것입니다. ka10038 요청에 strt_dt·end_dt를 쓰려면 명세대로 dt를 빈값 ''으로 보내야 합니다. 종목 여러 개를 장중에 돌리면 TR 2개 × 종목 수만큼 호출이 늘어 키움 429 호출 제한에 걸리기 쉬우니, 마감 뒤 하루 한 번 받는 구성을 권합니다.
창구 순위는 그 증권사 고객 전체 주문의 합이라 누가 샀는지 알려 주지 않으므로, 이 데이터만으로 매매 근거를 삼을 수는 없습니다. 이 글은 특정 종목이나 전략을 권하지 않습니다. 명세는 바뀔 수 있으니 키움 REST API 공식 문서로 현재 값을 확인하십시오.
5. 자주 묻는 질문
ka10042 응답에 순매수 수량이 없는데 어떻게 받나요?
ka10042 응답은 rank·mmcm_cd·mmcm_nm 세 필드뿐입니다. 수량은 같은 /api/dostk/rkinfo 경로의 ka10038(종목별증권사순위요청) acc_netprps_qty·buy_qty·sell_qty에서 받거나, ka10042에서 얻은 회원사코드를 3자리로 맞춰 ka10043(거래원매물대분석)에 넣어 일별 수량을 받습니다.
ka10042와 ka10038의 기간 dt 값은 왜 다른가요?
공식 명세상 ka10042는 5:5일, 10:10일, 20:20일처럼 일수를 그대로 쓰고, ka10038은 1:전일, 4:5일, 9:10일, 19:20일처럼 일수에서 1을 뺀 값을 씁니다. dt를 받는 10개 TR 중 이렇게 적힌 것은 ka10038뿐이므로, 기간은 일수로 들고 있다가 TR마다 변환하십시오.
ka10042의 mmcm_cd를 ka10043에 그대로 넣어도 되나요?
ka10042 응답 예시의 mmcm_cd는 '6'·'36'처럼 앞자리 0이 없고, ka10043·ka10039·ka10052·ka10078 요청은 길이 3에 ka10102 회원사 리스트('001' 형식)를 기준으로 합니다. 서버가 두 형식을 같게 받는지는 명세에 없으므로 zfill(3)으로 3자리로 맞춰 넘기는 편이 안전합니다.
ka10042로 순매도 상위 창구도 볼 수 있나요?
아니요. ka10042에는 매수·매도 방향 파라미터가 없고 순매수 순위만 줍니다. 순매도 상위는 ka10038에 qry_tp '1'(순매도순위정렬)을 넣어 받습니다.
창구 데이터를 묶어 쓰는 키움 봇, 만들어 드립니다
보고 싶은 종목과 기간만 알려 주시면 ka10042·ka10038·ka10043 연결, 코드·기간 변환, 이름 병합 검사까지 맞춰 드립니다. 24시간 빠른 답변 가능합니다.
무료 상담 시작하기