KIS Open API Runtime Hardening Playbook (Token, Throttle, WebSocket Discipline)
Date: 2026-02-25 (KST)
TL;DR
When you move from backtest to live KR execution, most outages are not alpha failures — they are control-plane failures:
- token churn
- bursty request spikes
- WebSocket reconnect storms
- silent subscription drops
This playbook turns official KIS constraints into deployable runtime guardrails for Vellab.
1) Official Constraints Snapshot (as of referenced notices)
1.1 REST / WebSocket base limits
From KIS official notice (2025.08.07 기준):
- REST (실전): 20 req/sec
- REST (모의): 2 req/sec
- Limit scope: per account(appkey)
- WebSocket: 1 session per appkey, and realtime registrations capped at 41 total (체결가+호가+예상체결+체결통보 합산)
1.2 Token endpoint constraint
Same official notice also states:
/oauth2/tokenP: 1 req/sec
1.3 WebSocket misuse enforcement risk
Official notice (2026-02-24) warns that repeated abnormal connect/disconnect loops can trigger temporary IP/appkey blocking.
Examples explicitly called out by KIS:
- connect then immediate close, repeatedly
- infinite subscribe/unsubscribe loops without validating data reception
1.4 Token lifecycle notes (FAQ + sample repo)
KIS FAQ post (접근토큰발급관리 샘플코드 안내) includes:
- token validity: 1 day
- refreshable after 6 hours
- historical issuance cadence guidance: 1 per 5 minutes (note in post)
Official sample repo README also mentions a practical reissue guidance line of ~1 per minute.
Practical implication: policy details have changed over time. Treat limits as versioned operational config, not hardcoded constants.
2) Production Policy for Ambiguous/Changing Limits
When official pages, FAQ posts, and sample code differ:
- Newest official notice wins
- If unresolved, run safe fallback mode (stricter pacing)
- Store active limits in external config (
limits.kis.json) for hot updates - Include
limit_versionin every order-path log line for forensic traceability
Suggested config shape:
{
"asOf": "2026-02-25",
"rest": { "realRps": 20, "paperRps": 2 },
"token": { "endpoint": "/oauth2/tokenP", "maxRps": 1, "cooldownMs": 60000 },
"ws": { "maxSessionsPerAppkey": 1, "maxRealtimeRegs": 41 }
}
3) Limiter Architecture (Do Not Use a Single Global Bucket)
Use independent limiters:
rest_real(20 rps target, reserve headroom)rest_paper(2 rps hard)token_issue(serialize + cooldown)ws_control_ops(subscribe/unsubscribe/connect budgets)
Why separate buckets?
Token refresh spikes should never starve order placement. WebSocket churn should never consume REST capacity.
4) Token Management: Singleflight + Stale-While-Valid
Recommended strategy:
- keep token in secure cache
- only refresh when
ttl < refreshThreshold - enforce singleflight lock so concurrent workers do not stampede
/tokenP - on refresh failure, keep using still-valid token until hard expiry
- add jittered retry for transient failures
Pseudo-flow:
if token.valid_for > threshold:
use cached token
else:
acquire distributed lock
if another worker already refreshed: use new token
else issue token with limiter + cooldown
5) WebSocket Discipline: Stateful, Not Impulsive
Implement explicit state machine:
IDLE -> CONNECTING -> AUTHENTICATED -> SUBSCRIBED -> STREAMING -> DEGRADED -> RECONNECT_BACKOFF
Guardrails:
- minimum session lifetime (don’t flap)
- bounded reconnect attempts per rolling window
- exponential backoff + random jitter
- verify
SUBSCRIBE SUCCESSbefore assuming feed is live - hard cap enforcement at 41 registrations
- LRU unsub policy when rotating symbols
If repeated abnormal loops are detected, trip a local breaker before KIS blocks you.
6) Failure Matrix (Operator-Friendly)
| Symptom | Likely Cause | Immediate Action | Long-term Fix |
|---|---|---|---|
| 403 in paper env | wrong host / wrong method | verify host + HTTP method | startup self-test for env/method mapping |
| MAX SUBSCRIBE OVER | exceeded 41 realtime regs | drop lowest-priority sub | dynamic subscription budget manager |
| sudden missing ticks | no subscribe success / invalid symbol | revalidate code + ack logs | subscription ACK audit + symbol validator |
| token storms | no singleflight | freeze refresh fan-out | distributed token lock + cooldown |
| frequent disconnects | reconnect loop bug | trigger breaker + sleep | ws finite-state reconnection policy |
7) SLO + Alerting Baseline
Minimum metrics:
kis_rest_req_total{env,endpoint,status}kis_rest_throttle_wait_ms{bucket}kis_token_issue_total{result}kis_ws_connect_total{result}kis_ws_subscribe_total{result}kis_ws_active_regskis_ws_reconnect_attempts_5m
Recommended alerts:
- token issues > N/hour
- ws reconnect attempts above threshold
- subscribe failure ratio > 1%
- any breaker-open event on live strategy
8) Rollout Plan for Vellab
- Build limiter/token/ws modules in shadow mode (observe only)
- Enable hard enforcement in paper
- Chaos test: forced disconnect, token expiry bursts, over-subscribe attempts
- Promote to real with conservative participation caps
- Weekly review of KIS notice board deltas and hot-update runtime limits
9) References
- KIS Intro (official): https://apiportal.koreainvestment.com/intro
- Service notice (rate limits, 2025.08.07 기준):
- Service notice (WebSocket abnormal loop block warning, 2026-02-24):
- FAQ (token management note):
- FAQ (rate-limit FAQ mirror post):
- Official sample repository:
One-line takeaway
Treat KIS connectivity as a risk engine: if token, throttle, and WebSocket hygiene are deterministic, your execution layer becomes boring — and boring is exactly what live trading needs.