Korea Investment Securities API: A Beginner's Guide to Your First Integration

🌐 한국어

Search for how to build a stock trading bot in Korea and the first thing you run into is the Korea Investment Securities API (KIS OpenAPI). Then you try to actually start, and you stall on the very first question: "What's an app key? And what's a token?" This article covers exactly that one step — from applying for access to your first successful quote lookup. Nothing more.

⚠️ This article is for educational and informational purposes only. It is not investment advice, and any losses are your own responsibility.

0. Three Things You Need

  • A Korea Investment Securities account — can be opened remotely. This is the account your bot will trade through.
  • Python 3.9 or later — the requests library is all you need.
  • A paper trading (mock investment) application — don't start on a live account. Paper trading is a practice environment where no real money moves.

1. Applying for API Access and Getting Your App Key

Apply for "OpenAPI" access on the KIS developer portal and you get two strings per account.

  • APP KEY — effectively your program's username
  • APP SECRET — the password for that username

Here's the mistake beginners make most: pasting these two values straight into the source code. The moment you push to GitHub, anyone can use your account's API. Always pull them from a separate config file or environment variables.

import os

# Read from environment variables (never hardcode in source)

APP_KEY    = os.environ["KIS_APP_KEY"]

APP_SECRET = os.environ["KIS_APP_SECRET"]

ACCOUNT_NO = os.environ["KIS_ACCOUNT_NO"]   # format: 8 digits-2 digits

# Paper trading server (a different host from live)

BASE_URL = "https://openapivts.koreainvestment.com:29443"

Paper trading and live trading use different domains entirely. If you're practicing but pointing at the live host, authentication won't pass — and the reverse is equally true if you send paper keys to the live host.

2. Getting an Access Token

The app key alone won't fetch you a quote. You present the app key and receive a pass in return — that pass is the access token. It's valid for roughly 24 hours.

import requests

def get_access_token():

    url = f"{BASE_URL}/oauth2/tokenP"

    body = {

        "grant_type": "client_credentials",

        "appkey":     APP_KEY,

        "appsecret":  APP_SECRET,

    }

    res = requests.post(url, json=body, timeout=10)

    res.raise_for_status()          # raises on any HTTP error

    return res.json()["access_token"]

token = get_access_token()

print("token prefix:", token[:10])   # never log the whole token

Printing the full token to your console or log file is a bad habit. If you need a sanity check, print the first few characters only.

One more thing — token issuance is rate-limited per day. Request a fresh one every time you run your code and you'll hit the ceiling fast. The standard approach is to cache the token to a file and only re-request it when the date rolls over.

import json, datetime, pathlib

CACHE = pathlib.Path("token_cache.json")   # add this to .gitignore

def load_token():

    today = datetime.date.today().isoformat()

    if CACHE.exists():

        data = json.loads(CACHE.read_text())

        if data.get("date") == today:

            return data["token"]           # reuse today's token

    token = get_access_token()

    CACHE.write_text(json.dumps({"date": today, "token": token}))

    return token

3. Your First Quote — Samsung Electronics' Current Price

Now you hold the pass, so you can request real data. The KIS API uses a value called tr_id to identify "which function am I calling." It differs per function, and sometimes differs between paper and live, so checking the docs is mandatory.

def get_price(token, code="005930"):      # 005930 = Samsung Electronics

    url = f"{BASE_URL}/uapi/domestic-stock/v1/quotations/inquire-price"

    headers = {

        "authorization": f"Bearer {token}",

        "appkey":        APP_KEY,

        "appsecret":     APP_SECRET,

        "tr_id":         "FHKST01010100",  # current stock quote

    }

    params = {

        "FID_COND_MRKT_DIV_CODE": "J",     # J = equities

        "FID_INPUT_ISCD":         code,

    }

    res = requests.get(url, headers=headers, params=params, timeout=10)

    res.raise_for_status()

    out = res.json()["output"]

    return {

        "name":       out["hts_kor_isnm"],

        "price":      int(out["stck_prpr"]),

        "change_pct": float(out["prdy_ctrt"]),

    }

print(get_price(load_token()))

# {'name': 'Samsung Electronics', 'price': 71000, 'change_pct': 0.85}

If that prints, your integration works. Everything else is a repeat of the same pattern — change the URL, change the tr_id, change the parameters.

4. Three Errors You Will Definitely Hit

  • EGW00123 / invalid token — either the token expired, or you sent a paper token to the live host. Check that your host and your keys are a matched pair first.
  • tr_id error — usually the right function but the wrong tr_id: some functions need the paper-trading variant, and a few of those differ in the leading characters.
  • Rate limit exceeded — loop through 100 tickers with no pause and you'll get cut off. Put something like time.sleep(0.2) between calls to space them out.

5. What Comes Next

Once that first lookup works, the natural progression is:

  1. Loop over several tickers and save the results to CSV
  2. Compute an indicator like a moving average on the saved data
  3. Build a dry run that logs instead of ordering when your conditions trigger
  4. Only after that's thoroughly validated, wire up the paper trading order API

Skipping step 3 and jumping straight to order code is the most expensive beginner mistake there is. Wire up ordering last.

Summary

  • App key and app secret go in environment variables, never in source
  • Paper and live differ in host and tr_id — keep them matched
  • The token is a 24-hour pass — cache it and reuse it
  • Your first goal is one thing: printing a current price successfully
  • The order API comes dead last

API integration isn't hard so much as unfamiliar. The moment a single price prints to your screen, it's just ordinary HTTP programming from there.

댓글

이 블로그의 인기 게시물

한국투자증권 KIS API로 실시간 시세 받기 (WebSocket 실전)

파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초

Go로 자동매매 신호봇 프레임워크 설계하기