Cloudwiki

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