엔드포인트 · TR_ID 전체 · 에러 코드 · 운영 패턴
T: 실전투자 (예: TTTC0802U)V: 모의투자 (예: VTTC0802U)FH: 시세 (실전/모의 공통, 예: FHKST01010100)| 헤더 | 값 |
|---|---|
content-type | application/json |
authorization | Bearer {access_token} |
appkey | 발급받은 36자리 AppKey |
appsecret | 발급받은 180자리 AppSecret |
tr_id | 호출하려는 TR_ID |
hashkey 헤더 추가 필수. Hashkey 생성 절차는 Section 2 참조.
url = 'https://openapivts.koreainvestment.com:29443/oauth2/tokenP' body = { 'grant_type': 'client_credentials', 'appkey': APP_KEY, 'appsecret': APP_SECRET } response = requests.post(url, json=body, timeout=10) token = response.json()['access_token']
주문 API 호출 전에 반드시 실행. 주문 Body를 먼저 해시 엔드포인트에 보내 HASH를 받고, 그 HASH를 실제 주문의 헤더에 첨부.
hashkey 헤더에 설정.EGW00121 에러def get_hashkey(body: dict) -> str: """주문 Body로 HashKey 생성.""" url = 'https://openapivts.koreainvestment.com:29443/uapi/hashkey' headers = { 'content-type': 'application/json', 'appkey': APP_KEY, 'appsecret': APP_SECRET } r = requests.post(url, json=body, headers=headers) return r.json()['HASH'] # 사용 예: 매수 주문 order_body = { 'CANO': CANO, 'ACNT_PRDT_CD': '01', 'PDNO': '005930', 'ORD_DVSN': '01', 'ORD_QTY': '10', 'ORD_UNPR': '0' } hash_key = get_hashkey(order_body) headers = { 'content-type': 'application/json', 'authorization': f'Bearer {token}', 'appkey': APP_KEY, 'appsecret': APP_SECRET, 'tr_id': 'VTTC0802U', 'hashkey': hash_key # 여기! } # order_body는 완전히 동일한 것을 재사용 response = requests.post(order_url, json=order_body, headers=headers)
TTTC0802U. HashKey 필수.| 코드 | 의미 |
|---|---|
00 | 지정가 (ORD_UNPR 필수) |
01 | 시장가 (ORD_UNPR="0") |
02 | 조건부지정가 |
03 | 최유리지정가 |
04 | 최우선지정가 |
05 | 장전 시간외 |
06 | 장후 시간외 |
VTTC0801U로 변경. Body 구조 동일.tr_id = 'VTTC0802U' if side == 'buy' else 'VTTC0801U'. n8n에서: IF 노드 또는 Set 노드 조건부.| 기능 | 모의 | 실전 | HTTP |
|---|---|---|---|
| 시세 | 공통 | ||
| 주식 현재가 | FHKST01010100 | GET | |
| 주식 호가/예상체결 | FHKST01010200 | GET | |
| 일자별 시세 | FHKST01010400 | GET | |
| 기간별 시세 | FHKST03010100 | GET | |
| 투자자별 매매 | FHKST01010900 | GET | |
| 체결가 조회 | FHKST01010300 | GET | |
| 주문 | |||
| 현금 매수 | VTTC0802U | TTTC0802U | POST |
| 현금 매도 | VTTC0801U | TTTC0801U | POST |
| 정정/취소 | VTTC0803U | TTTC0803U | POST |
| 신용 매수 | VTTC0852U | TTTC0852U | POST |
| 신용 매도 | VTTC0851U | TTTC0851U | POST |
| 계좌/잔고 | |||
| 잔고 조회 | VTTC8434R | TTTC8434R | GET |
| 주문 가능 | VTTC8908R | TTTC8908R | GET |
| 일별 주문체결 | VTTC8001R | TTTC8001R | GET |
| 정정취소가능 주문 | VTTC0081R | TTTC0081R | GET |
| 해외주식 (참고) | |||
| 해외 현재가 | HHDFS00000300 | GET | |
| 해외 매수 (미국) | VTTT1002U | TTTT1002U | POST |
| 해외 매도 (미국) | VTTT1006U | TTTT1006U | POST |
| 코드 | 원인 + 대응 |
|---|---|
EGW00121 | HashKey 불일치 — 주문 Body와 HashKey 생성 Body가 다름. 완전히 동일한 객체로 두 번 호출할 것 |
EGW00123 | 토큰 만료. 재발급 필요 (expires_in 체크) |
EGW00133 | 앱키·시크릿 오류 또는 승인 대기 중. KIS Developers 포털 확인 |
EGW00201 | 호출 유량 초과 (Rate Limit) — 실전 20TPS / 모의 2TPS. Wait 노드 또는 sleep 추가 |
EGW00205 | 요청 타임아웃. 네트워크 or API 장애. 재시도 |
EGW00007 | 필수 파라미터 누락. Query/Body 재확인 |
APBK0013 | (정상) 주문 전송 완료. 에러 아님 |
APBK0912 | 매수 가능 금액 부족. 예수금 or 미체결 주문 확인 |
APBK0919 | 매도 가능 수량 부족. 실제 보유 수량 재확인 |
APBK0916 | 주문 가격 오류. 호가 단위(틱 사이즈) 준수 필요 |
APBK0650 | 장 마감 후 주문. 정규장 시간 확인 (09:00-15:30) |
APBK7005 | 시장가 + 가격 입력 오류. 시장가는 ORD_UNPR="0" |
APBK0511 | 거래정지 종목. 종목 상태 확인 |
APBK0522 | 상한가/하한가 도달. 지정가 조정 |
OPSP0002 | TR_ID와 URL 불일치. 엔드포인트 재확인 |
OPSP0003 | 실전/모의 환경 불일치 (T/V 접두어 실수) |
OPSP1001 | 계좌번호 오류. CANO 8자리 + ACNT_PRDT_CD 2자리 |
401 Unauthorized | 헤더 authorization 누락 또는 잘못된 Bearer 토큰 |
403 Forbidden | 권한 없음. KIS Developers 서비스 승인 필요 or 모의투자 신청 |
500 Internal Server Error | KIS 서버 장애. 잠시 후 재시도. 지속 시 고객센터 문의 |
{
"rt_cd": "1", // 0=성공, 1=실패
"msg_cd": "EGW00121", // 에러 코드
"msg1": "해쉬키가 일치하지 않습니다."
}
rt_cd가 "0"이 아니면 에러. msg_cd로 분류 → Rate Limit이면 재시도, 토큰 만료면 재발급, 나머지는 Discord 알림.
def handle_kis_response(response): data = response.json() if data.get('rt_cd') == '0': return data['output'] code = data.get('msg_cd') if code == 'EGW00201': time.sleep(2) raise RetryableError('Rate limit') elif code == 'EGW00123': refresh_token() raise RetryableError('Token expired') elif code in ['APBK0912', 'APBK0919']: notify_discord(f'자금/수량 부족: {data[\"msg1\"]}') raise Exception(f'KIS Error {code}: {data[\"msg1\"]}')
| 환경 | 초당 호출 수 (TPS) | 비고 |
|---|---|---|
| 실전투자 | 20 TPS | 시세·주문 모두 합산 |
| 모의투자 | 2 TPS | 실전보다 엄격. 0.5초 간격 보장 필요 |
| 토큰 발급 | 1일 ≈ 24회 권장 | 과도 시 장기 차단 가능 |
EGW00201. 반복문에서 time.sleep(0.6) 또는 n8n Wait 노드 0.6초 필수 삽입.
EGW00201 받으면 exponential backoff: 1초 → 2초 → 4초 → 8초 순으로 재시도. 최대 3회.
| 시간대 | 주문 가능 여부 |
|---|---|
| 정규장 09:00 ~ 15:30 | ✓ 즉시 체결 (시장가) / 지정가 대기 |
| 시간외 단일가 16:00 ~ 18:00 | ✓ 별도 TR_ID (ORD_DVSN: 06) |
| 장전 시간외 08:30 ~ 08:40 | ✓ ORD_DVSN: 05 |
| 주말·공휴일 | ✗ 대기 주문도 안 됨 (APBK0650) |