CloudStock/한국투자증권KIS OpenAPI
# 한국투자증권 (KIS) Open API 스펙 정리
> 시세 조회 · 종목 정보 | Cloudflare Workers 환경 기준
---
## 공통 정보
| 항목 | 값 |
|------|-----|
| **실전 도메인** | `https://openapi.koreainvestment.com:9443` |
| **모의 도메인** | `https://openapivts.koreainvestment.com:29443` |
| **인증 방식** | OAuth 2.0 (Bearer Token) |
| **요청 형식** | REST / JSON |
| **TLS** | TLS 1.2 이상 필수 (1.0/1.1 2025.12.12 이후 지원 종료) |
---
## 인증 (OAuth)
### 접근 토큰 발급
```
POST /oauth2/tokenP
```
**Request Headers**
```
Content-Type: application/json
```
**Request Body**
```json
{
"grant_type": "client_credentials",
"appkey": "{APP_KEY}",
"appsecret": "{APP_SECRET}"
}
```
**Response**
```json
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 86400,
"access_token_token_expired": "2024-01-02 00:00:00"
}
```
> ⚠️ **토큰 유효기간**: 일반 개인고객 **1일**
> Cloudflare Workers KV에 캐싱 후 만료 전 갱신 권장
**Workers 예시 (토큰 발급 + KV 캐싱)**
```js
async function getAccessToken(env) {
// KV에서 캐시된 토큰 조회
const cached = await env.KV.get('kis_token');
if (cached) return cached;
const res = await fetch('https://openapi.koreainvestment.com:9443/oauth2/tokenP', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'client_credentials',
appkey: env.APP_KEY,
appsecret: env.APP_SECRET,
}),
});
const data = await res.json();
// 만료 1시간 전에 갱신되도록 TTL 설정
await env.KV.put('kis_token', data.access_token, { expirationTtl: 82800 });
return data.access_token;
}
```
---
## API 1 — 주식 현재가 시세
```
GET /uapi/domestic-stock/v1/quotations/inquire-price
```
**TR ID**: `FHKST01010100`
### Request Headers
| 헤더 | 값 | 설명 |
|------|-----|------|
| `Content-Type` | `application/json` | |
| `authorization` | `Bearer {access_token}` | 발급된 토큰 |
| `appkey` | `{APP_KEY}` | |
| `appsecret` | `{APP_SECRET}` | |
| `tr_id` | `FHKST01010100` | 거래 ID |
### Request Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `fid_cond_mrkt_div_code` | String | ✅ | 시장 구분: `J` (주식/ETF/ETN), `ETF`, `ETN` |
| `fid_input_iscd` | String | ✅ | 종목코드 6자리 (예: `005930`) |
### Response Body — `output` 주요 필드
| 필드명 | 타입 | 설명 |
|--------|------|------|
| `stck_prpr` | String | **주식 현재가** |
| `prdy_vrss` | String | 전일 대비 등락 금액 |
| `prdy_vrss_sign` | String | 부호 (1:상한/2:상승/3:보합/4:하한/5:하락) |
| `prdy_ctrt` | String | 전일 대비 등락률 (%) |
| `stck_oprc` | String | 시가 |
| `stck_hgpr` | String | 고가 |
| `stck_lwpr` | String | 저가 |
| `stck_mxpr` | String | 상한가 |
| `stck_llam` | String | 하한가 |
| `acml_vol` | String | 누적 거래량 |
| `acml_tr_pbmn` | String | 누적 거래 대금 |
| `rprs_mrkt_kor_name` | String | 대표시장명 (예: KOSPI200) |
| `bstp_kor_isnm` | String | 업종 한글명 |
| `hts_frgn_ehrt` | String | 외국인 보유 비율 (%) |
| `frgn_ntby_qty` | String | 외국인 순매수 수량 |
| `wghn_avrg_stck_prc` | String | 가중 평균 주가 |
**Response 예시**
```json
{
"rt_cd": "0",
"msg_cd": "MCA00000",
"msg1": "정상처리 되었습니다.",
"output": {
"stck_prpr": "128500",
"prdy_vrss": "0",
"prdy_vrss_sign": "3",
"prdy_ctrt": "0.00",
"stck_oprc": "128500",
"stck_hgpr": "130000",
"stck_lwpr": "128500",
"stck_mxpr": "167000",
"stck_llam": "90000",
"acml_vol": "2669075",
"acml_tr_pbmn": "344570137500",
"rprs_mrkt_kor_name": "KOSPI200",
"bstp_kor_isnm": "전기.전자",
"hts_frgn_ehrt": "49.48"
}
}
```
**Workers 호출 예시**
```js
async function getStockPrice(code, env) {
const token = await getAccessToken(env);
const url = new URL('https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/quotations/inquire-price');
url.searchParams.set('fid_cond_mrkt_div_code', 'J');
url.searchParams.set('fid_input_iscd', code);
const res = await fetch(url.toString(), {
headers: {
'Content-Type': 'application/json',
'authorization': `Bearer ${token}`,
'appkey': env.APP_KEY,
'appsecret': env.APP_SECRET,
'tr_id': 'FHKST01010100',
},
});
const data = await res.json();
return data.output;
}
```
---
## API 2 — 주식 현재가 기본 정보 (종목 정보)
```
GET /uapi/domestic-stock/v1/quotations/search-stock-info
```
**TR ID**: `CTPF1002R`
### Request Headers
공통 헤더 동일 + `tr_id: CTPF1002R`
### Request Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `PRDT_TYPE_CD` | String | ✅ | 상품유형코드: `300` (주식) |
| `PDNO` | String | ✅ | 종목코드 6자리 |
### Response Body — `output` 주요 필드
| 필드명 | 타입 | 설명 |
|--------|------|------|
| `prdt_name` | String | **종목명** |
| `prdt_eng_name` | String | 종목 영문명 |
| `pdno` | String | 종목코드 |
| `prdt_type_cd` | String | 상품유형코드 |
| `lstg_stqt` | String | 상장 주수 |
| `lstg_dt` | String | 상장일 |
| `mrkt_id_cd` | String | 시장 ID 코드 (KSP: KOSPI / KSQ: KOSDAQ) |
| `stck_fcam` | String | 주식 액면가 |
| `cpfn` | String | 자본금 |
| `ssts_hot_yn` | String | 공매도 과열 여부 (Y/N) |
| `short_over_yn` | String | 단기과열 여부 (Y/N) |
---
## API 3 — 종목 기본 정보 (시세용 종목 검색)
```
GET /uapi/domestic-stock/v1/quotations/search-info
```
**TR ID**: `CTPF1604R`
종목코드 대신 **종목명(한글/영문)** 으로 검색 가능.
### Request Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `PDNO` | String | ✅ | 종목코드 또는 종목명 |
| `PRDT_TYPE_CD` | String | ✅ | `300` (주식) |
---
## 공통 응답 구조
모든 API 응답에 포함되는 공통 필드:
| 필드 | 설명 |
|------|------|
| `rt_cd` | 결과 코드. `"0"` = 정상 |
| `msg_cd` | 메시지 코드 |
| `msg1` | 결과 메시지 |
| `output` | 실제 데이터 (단건) |
| `output1` | 실제 데이터 (복수, API에 따라 다름) |
| `output2` | 추가 데이터 (API에 따라 다름) |
---
## 오류 처리
```js
const data = await res.json();
if (data.rt_cd !== '0') {
throw new Error(`KIS API 오류: [${data.msg_cd}] ${data.msg1}`);
}
```
주요 오류 코드:
| msg_cd | 설명 |
|--------|------|
| `EGW00123` | 접근 토큰 만료 → 재발급 필요 |
| `EGW00121` | appkey/appsecret 오류 |
| `APBK0918` | 조회 건수 초과 (Rate Limit) |
---
## Rate Limit
| 구분 | 제한 |
|------|------|
| 시세 조회 (REST) | 초당 20회 |
| 모의 계좌 | 제한 더 낮음 (실전 계좌 권장) |
> Workers에서 다수 종목 조회 시 `Promise.all` 병렬 처리보다 순차 호출 또는 적절한 딜레이 추가 권장
---
## 참고 링크
- 공식 개발자 센터: https://apiportal.koreainvestment.com
- 공식 GitHub 샘플: https://github.com/koreainvestment/open-trading-api
- API 신청: https://securities.koreainvestment.com/main/customer/systemdown/RestAPIService.jsp