KIS API 계좌번호 오류 — CANO·ACNT_PRDT_CD
12345678-01로 표시되는 계좌번호라면
앞 8자리 12345678이 CANO(종합계좌번호),
뒤 2자리 01이 ACNT_PRDT_CD(계좌상품코드)입니다.
하이픈은 빼고, 둘 다 문자열로 넣습니다.
값을 맞게 적었는데도 계속 실패한다면 원인은 대개 값이 아니라 타입입니다 —
설정 파일에 따옴표 없이 적어 01이 정수 1로 읽히면서
앞자리 0이 사라진 것이 가장 흔합니다.
KIS API 발급 가이드대로
appkey·appsecret을 받고 access_token까지 정상 발급됐는데,
막상 잔고를 조회하거나 주문을 넣으면 실패하는 경우가 있습니다.
토큰은 멀쩡하니 인증 문제는 아닌 것 같고,
에러코드 목록을 봐도 딱 떨어지는 항목이 안 보입니다.
이럴 때 가장 먼저 의심할 곳이 계좌 파라미터입니다.
이 글은 CANO와 ACNT_PRDT_CD 하나만 다룹니다.
이 글의 순서
- 증상 — 응답이 어떻게 오나
- 정답 — 8자리와 2자리로 나눈다
- 실제로 나는 실수 5가지
- 설정 파일에 쓰는 법
- 검증 함수 — 눈으로 보지 말고 코드로 센다
- 어느 요청에 들어가나 · 체크리스트 · FAQ
1. 증상 — 응답이 어떻게 오나
KIS API의 모든 응답에는 rt_cd·msg_cd·msg1 세 필드가 있습니다.
계좌 파라미터가 잘못됐을 때는 HTTP 상태코드는 200인데
rt_cd가 0이 아닌 형태로 옵니다.
즉 파이썬에서 예외가 안 납니다.
res.raise_for_status()만 걸어 둔 코드는 이 실패를 그냥 통과시킵니다.
res = requests.get(url, headers=headers, params=params, timeout=5)
print(res.status_code) # 200 ← 여기서 통과해 버린다
data = res.json()
print(data["rt_cd"]) # "0"이 아니다
print(data["msg_cd"], data["msg1"]) # 메시지에 '계좌' 관련 문구가 담겨 온다
먼저 할 일. 모든 호출을 아래처럼 감싸 두면
계좌 문제인지 다른 문제인지가 로그 한 줄로 갈립니다.
msg_cd는 호출한 tr_id와 상황에 따라 달라지므로,
코드를 외우지 말고 그때그때 찍어서 확인하는 편이 빠릅니다.
def check(res):
"""KIS 응답 표준 검증 — rt_cd가 0이 아니면 즉시 중단"""
data = res.json()
if data.get("rt_cd") != "0":
raise RuntimeError(
f"KIS 에러 [{data.get('msg_cd')}] {data.get('msg1','').strip()}"
)
return data
2. 정답 — 8자리와 2자리로 나눈다
한국투자증권 계좌번호는 종합계좌번호 8자리 + 상품코드 2자리 구조입니다. KIS API는 이 둘을 별도 파라미터로 받습니다.
| 파라미터 | 무엇 | 자릿수 | 예시 |
|---|---|---|---|
CANO | 종합계좌번호 (앞부분) | 8자리 | "12345678" |
ACNT_PRDT_CD | 계좌상품코드 (뒷부분) | 2자리 | "01" |
ACCOUNT = "12345678-01" # 앱에 보이는 그대로
cano, acnt_prdt_cd = ACCOUNT.split("-")
print(repr(cano), repr(acnt_prdt_cd)) # '12345678' '01'
params = {
"CANO": cano, # 문자열
"ACNT_PRDT_CD": acnt_prdt_cd, # 문자열 — "1"이 아니라 "01"
# ... 나머지 조회·주문 파라미터
}
3. 실제로 나는 실수 5가지
① 하이픈을 그대로 넣는다
"12345678-01"을 CANO에 통째로 넣는 경우입니다.
가장 단순하지만 가장 흔합니다. 하이픈은 표시용이고 API로는 보내지 않습니다.
② 정수로 변환해 앞자리 0이 날아간다
이 글에서 가장 중요한 항목입니다.
상품코드 01을 숫자로 다루는 순간 1이 됩니다.
종합계좌번호가 0으로 시작하는 계좌라면 8자리가 7자리가 됩니다.
>>> str(int("01"))
'1' # ← 두 자리가 한 자리로. 이대로 보내면 계좌를 못 찾는다
>>> str(int("01234567"))
'1234567' # ← 8자리가 7자리로
따옴표 하나 때문에 생깁니다. YAML·JSON 설정 파일에서
acnt_prdt_cd: 01처럼 따옴표 없이 적으면 파서가 숫자로 읽습니다.
코드는 멀쩡하고 값도 맞게 적었는데 실패하므로 원인을 찾는 데 제일 오래 걸리는 종류입니다.
반드시 acnt_prdt_cd: "01"로 따옴표를 붙이십시오.
③ 상품코드를 01로 고정해 버린다
예제 코드 대부분이 "01"로 되어 있어서 그대로 상수로 박아 두는 경우입니다.
상품코드는 계좌 종류를 구분하는 값이라 계좌마다 다를 수 있습니다.
한 사람이 계좌를 여러 개 가지고 있으면 특히 그렇습니다.
자기 앱에 표시된 계좌번호의 뒤 두 자리를 그대로 쓰십시오.
④ 실전 계좌번호로 모의투자를 호출한다
모의투자는 계좌번호·appkey·appsecret이 실전과 완전히 별개입니다.
실전 계좌번호를 넣고 모의 도메인으로 부르면 형식은 멀쩡한데 계좌를 못 찾습니다.
tr_id도 세트로 바뀝니다 — 자세한 전환 목록은
모의투자 실전 전환 6가지에 정리해 두었습니다.
구조로 막는 법. 계좌번호·도메인·tr_id를
환경 하나에 묶어 두고, 코드 어디에서도 개별로 바꾸지 못하게 하십시오.
세 값이 따로 놀 수 있는 구조면 언젠가 반드시 어긋납니다.
⑤ 공백·줄바꿈이 섞여 있다
설정 파일이나 .env에서 값을 읽을 때 뒤에 공백이나 줄바꿈이 붙어 오는 경우입니다.
"12345678 "은 눈으로는 정상이고 길이만 9입니다.
항상 .strip()을 거치십시오.
4. 설정 파일에 쓰는 법
한국투자증권 공식 예제는 설정을 별도 파일로 분리하는 방식을 씁니다. 같은 형태로 쓰되 계좌 값 두 개를 반드시 따옴표로 감쌉니다.
# kis_config.yaml
# ── 실전 ─────────────────────────────
prod_appkey: "PSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
prod_appsecret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
prod_cano: "12345678" # ← 따옴표 필수
prod_acnt_prdt: "01" # ← 따옴표 없으면 1로 읽힌다
# ── 모의 ─────────────────────────────
paper_appkey: "PSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
paper_appsecret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
paper_cano: "50123456"
paper_acnt_prdt: "01"
이 파일은 절대 저장소에 올리지 마십시오.
appkey·appsecret과 계좌번호가 한 파일에 같이 들어 있습니다.
.gitignore 등록과 키 관리 원칙은
API 키 보안에 정리해 두었습니다.
5. 검증 함수 — 눈으로 보지 말고 코드로 센다
계좌 파라미터 문제는 보면 정상으로 보이기 때문에 눈으로 찾기 어렵습니다. 봇 기동 시점에 길이와 타입을 한 번 검사하고, 어긋나면 그 자리에서 멈추게 하십시오. 잘못된 계좌 값으로 주문 루프에 진입하는 것보다 훨씬 낫습니다.
def validate_account(cano, acnt_prdt_cd):
"""봇 기동 시 1회 — 여기서 못 잡으면 주문 루프에서 터진다"""
for name, val, size in (("CANO", cano, 8), ("ACNT_PRDT_CD", acnt_prdt_cd, 2)):
if not isinstance(val, str):
raise ValueError(f"{name}: 문자열이 아님 ({type(val).__name__}) "
f"— 설정 파일에 따옴표를 빠뜨렸을 가능성")
if val != val.strip():
raise ValueError(f"{name}: 앞뒤 공백/줄바꿈 포함 → {val!r}")
if len(val) != size:
raise ValueError(f"{name}: {size}자리가 아님 (현재 {len(val)}자리) → {val!r}")
if not val.isdigit():
raise ValueError(f"{name}: 숫자 외 문자 포함(하이픈?) → {val!r}")
return True
validate_account("12345678", "01") # OK
validate_account("12345678", 1) # ValueError: 문자열이 아님 (int)
validate_account("12345678-01", "01")# ValueError: 8자리가 아님 (11자리)
효과. 이 함수 하나가 실수 ①·②·⑤를 전부 기동 3초 안에 잡아냅니다. 남은 ③·④는 값이 형식상 정상이라 코드로는 못 잡으니, 실행 환경 이름을 로그에 찍어 사람이 보게 하는 편이 낫습니다.
계좌 연결부터 주문·잔고까지 동작하는 상태로 만들어 드립니다. 24시간 빠른 답변 가능합니다.
무료 상담 시작하기 →6. 어느 요청에 들어가나
계좌를 특정해야 하는 요청은 거의 전부입니다. 시세 조회처럼 계좌와 무관한 요청에는 들어가지 않습니다.
- 잔고 조회 — 보유 종목이 잘려 나오는 문제는 계좌가 아니라 페이지 제한입니다 → 연속조회 처리
- 매수·매도 주문 — 계좌 파라미터에 더해
hashkey가 얽힙니다 → hashkey는 왜 쓰나 - 주문 취소·정정 — 원주문번호와 함께 계좌를 다시 넣습니다 → 취소·정정 처리
- 체결 내역·예수금 조회 — 동일하게
CANO·ACNT_PRDT_CD필요
반대로 현재가 조회 같은 시세 API에는 계좌가 필요 없습니다. "시세는 되는데 잔고만 안 된다"면 계좌 파라미터를 의심하라는 신호로 읽으면 됩니다.
체크리스트
- 계좌번호를 8자리 + 2자리로 나눴는가
- 하이픈을 뺐는가
- 설정 파일에서 두 값에 따옴표를 붙였는가
int()를 거치는 경로가 없는지 확인했는가- 상품코드를
"01"로 고정하지 않고 내 계좌 값을 썼는가 - 계좌번호·도메인·
tr_id가 같은 환경(실전/모의)으로 묶여 있는가 rt_cd를 검사하는가 (HTTP 200만 보고 넘어가지 않는가)- 기동 시 검증 함수가 도는가
자주 묻는 질문
Q. CANO와 ACNT_PRDT_CD는 각각 무엇인가요?
계좌번호를 나눈 것입니다. CANO는 앞 8자리 종합계좌번호,
ACNT_PRDT_CD는 뒤 2자리 계좌상품코드입니다. 하이픈은 넣지 않고 둘 다 문자열로 보냅니다.
Q. 값은 맞는데 계속 오류가 납니다.
값보다 타입을 보십시오. 설정 파일에 따옴표를 빠뜨려 "01"이
정수 1로 읽히는 경우가 가장 흔합니다. 그다음이 하이픈 포함, 공백·줄바꿈 혼입입니다.
Q. ACNT_PRDT_CD는 항상 01인가요?
아닙니다. 예제에 자주 쓰이는 값일 뿐 계좌마다 다를 수 있습니다. 앱에 표시된 계좌번호의 뒤 두 자리를 그대로 쓰고, 코드 체계는 한국투자증권 공식 안내에서 확인하십시오.
Q. 모의투자와 실전투자는 계좌번호가 다른가요?
다릅니다. 앱키·앱시크릿도 별개이고 tr_id와 도메인까지 세트로 바뀝니다.
셋을 한 환경으로 묶어 관리하십시오.
확인 캐치. 이 글의 파라미터명과 자릿수 규칙은 2026년 8월 12일 기준 한국투자증권 KIS Developers의 국내주식 주문·잔고 계열 API가 계좌를 받는 방식에 근거합니다. 상품코드 값 체계와 API 스펙은 증권사 사정으로 변경될 수 있으므로 구현 전 KIS Developers 공식 문서와 본인 계좌 정보에서 실제 값을 확인하십시오. 본 글은 특정 종목이나 수익률에 대한 어떠한 예측이나 권유도 담고 있지 않습니다.
마무리
정리하면 한 줄입니다.
계좌번호는 앞 8자리 CANO와 뒤 2자리 ACNT_PRDT_CD로 나누고, 둘 다 문자열로 보낸다.
그리고 실패의 대부분은 값이 아니라 따옴표 하나에서 옵니다.
인증 쪽이 함께 흔들리고 있다면 토큰 만료·재발급 규칙도 같이 확인해 보십시오.