X-KAPI-KEY 헤더, timestamp 파라미터, signature 파라미터가 필요합니다.GET/DELETE: 입력 값을 URL query string으로 보냅니다.POST: 입력 값을 application/x-www-form-urlencoded 본문으로 보내고 Content-Type: application/x-www-form-urlencoded 헤더를 포함합니다.signature를 덧붙입니다.queryString + bodyString, 중간에 & 추가 없이) 서명합니다. 가능하면 서명 대상 파라미터를 한 쪽에 모아두세요.서명 입력 예시 (query + body 결합):
timestamp=1719232467910symbol=btc_krw
응답 헤더로 잔여 quota를 추적합니다.
Ratelimit: limit=50, remaining=48, reset=1Ratelimit-Policy: 50;w=1Retry-After가 반환될 수 있습니다.| API 그룹 | 제한 |
|---|---|
| 공개 REST | IP당 초당 50 요청 |
| 주문 신청 | 계정당 초당 30 요청 |
| 주문 취소 | 계정당 초당 30 요청 |
| 입출금 신청 | 계정당 초당 5 요청 |
| 그 외 비공개 REST | 계정당 초당 50 요청 |
HTTP 429를 받으면 Retry-After 또는 Ratelimit reset 시점까지 기다린 뒤 재시도합니다.
비공개 요청에는 다음을 포함합니다.
timestamp: 현재 Unix 시각(ms).recvWindow: 선택값. 유효 윈도우(ms). 기본 5000, 최대 60000.서버는 다음 조건을 만족할 때에만 요청을 수락합니다.
serverTime - timestamp <= recvWindow
timestamp < serverTime + 1000
클라이언트 시각이 어긋나면 EXCEED_TIME_WINDOW 오류가 반환됩니다. 이 윈도우는 비대칭입니다 — recvWindow는 과거 방향만 넓히고, 미래 경계는 recvWindow로 넓힐 수 없는 고정 +1000 ms입니다 — 따라서 시각이 서버보다 조금이라도 앞서면 모든 서명 요청이 실패하며 recvWindow를 키워도 소용이 없습니다. 원시 호스트 시각이 아니라 Korbit 서버 시각(GET /v2/time)을 기준으로 서명하세요. 복원력 recipe 6을 참고하세요.
Node.js 예제. 실제로 전송할 URLSearchParams를 그대로 서명한 뒤 signature를 덧붙입니다.
import crypto from "node:crypto";
const BASE_URL = "https://api.korbit.co.kr";
const apiKey = process.env.KORBIT_API_KEY;
const apiSecret = process.env.KORBIT_API_SECRET;
function signHmac(encodedParams) {
return crypto.createHmac("sha256", apiSecret).update(encodedParams, "utf8").digest("hex");
}
async function korbitPrivate(method, path, params = {}) {
const clean = Object.fromEntries(
Object.entries(params).filter(([, v]) => v !== undefined && v !== null && v !== "")
);
const p = new URLSearchParams({ ...clean, timestamp: String(Date.now()) });
const signature = signHmac(p.toString());
p.append("signature", signature);
const headers = { "X-KAPI-KEY": apiKey };
let url = `${BASE_URL}${path}`;
const init = { method, headers };
if (method === "GET" || method === "DELETE") {
url += `?${p.toString()}`;
} else {
headers["Content-Type"] = "application/x-www-form-urlencoded";
init.body = p.toString();
}
const res = await fetch(url, init);
const json = await res.json().catch(() => ({}));
if (!res.ok || json.success === false) {
const err = new Error(`Korbit API error ${res.status}`);
err.status = res.status; // 숫자 HTTP 상태 코드
err.code = json?.error?.message; // 심볼릭 코드, 예: "DUPLICATE_CLIENT_ORDER_ID"
err.body = json;
throw err;
}
return json.data ?? json;
}
실패 시 Korbit은 { "success": false, "error": { "code": <httpStatus>, "message": "<symbolic code>" } }를 반환합니다. 분기 판단에 쓰는 심볼릭 코드(EXCEED_TIME_WINDOW, DUPLICATE_CLIENT_ORDER_ID, NO_BALANCE 등)는 error.message이며, error.code가 아닙니다 — error.code는 숫자 HTTP 상태 코드가 반복된 값입니다. 최상위에는 심볼릭 값이 없습니다. 위 헬퍼는 이 심볼릭 코드를 err.code로 노출하므로 호출부는 그 값으로 분기하면 됩니다.
개인키는 PEM 문자열로 소스 외부에 보관합니다.
import crypto from "node:crypto";
const apiKey = process.env.KORBIT_API_KEY;
const privateKeyPem = process.env.KORBIT_ED25519_PRIVATE_KEY;
function signEd25519(encodedParams) {
const sig = crypto.sign(null, Buffer.from(encodedParams), privateKeyPem);
return sig.toString("base64");
}
URLSearchParams에 Base64 서명을 append 하면 URL 인코딩이 자동으로 적용됩니다.
POST /v2/orders에 symbol, side(buy/sell), orderType을 보냅니다. 주문 크기를 지정하는 필드는 주문 유형에 따라 다르며, 이 부분이 가장 흔한 실수 지점이므로 정확히 맞춰야 합니다:
| orderType | 필수 사이징 필드 | 생략 |
|---|---|---|
limit |
price 와 qty |
amt |
market / best 매수(buy) |
amt(상대자산 매수 금액 — quote currency, symbol의 두 번째 항목) |
price, qty |
market / best 매도(sell) |
qty(기준자산 수량 — base currency, symbol의 첫 번째 항목) |
price, amt |
즉, btc_krw를 50,000 KRW어치 시장가 매수할 때는 amt=50000을 보내고 qty와 price는 보내지 않습니다. amt는 해당 페어의 상대자산 단위입니다. 0.01 BTC 시장가 매도는 qty=0.01만 보내고 amt/price는 생략합니다. best(BBO) 주문은 추가로 timeInForce와 bestNth가 필요합니다.
clientOrderId([0-9a-zA-Z.:_-]{1,36} 형식)를 넣으면 멱등성이 보장됩니다. 같은 clientOrderId로 여러 번 요청해도 한 번만 처리되며, 이후 GET /v2/orders?clientOrderId=...로 조회할 수 있습니다. 주문이 성공하면 부여된 orderId가 반환되며, 주문의 나머지 상태는 후속 GET /v2/orders로 읽습니다(아래 참고). clientOrderId는 사실상 해제되지 않으므로, 서로 다른 주문에 같은 ID를 재사용하지 말고 신청마다 충돌에 강한 ID를 새로 발급해 영속화하세요 — 리질리언스 #1 참고.
GET /v2/orders(orderId 또는 clientOrderId로 조회)는 체결 상태를 포함한 주문 정보를 반환합니다. 금액 필드는 문자열이므로 문자열/decimal로 유지하세요.
| 필드 | 의미 | 비고 |
|---|---|---|
qty |
주문 수량 | 시장가 매수(=amt로 사이징)에서는 없음 |
amt |
매수 금액(시장가 매수) | 상대자산, 예: KRW |
filledQty |
체결된 수량 | 체결 전에는 "0" |
filledAmt |
체결된 상대자산 금액 | 예: 사용/수령한 KRW |
avgPrice |
평균 체결가 | 선택적 — 체결이 생기기 전에는 없음. 가드 없이 읽지 말 것 |
status |
주문 상태 | 아래 주문 상태 참고(partiallyFilled 등) |
체결을 안전하게 다루려면 avgPrice가 없을 수 있다고 보고(필요하고 없을 때는 filledAmt / filledQty를 decimal로 계산해 도출하며, "미체결"은 평균가 없음으로 처리 — 절대 NaN이 되지 않게), 미체결 잔량은 qty - filledQty로 계산하되 음수가 되지 않게 합니다. partiallyFilled 주문은 filledQty가 qty보다 작으므로, 후속 동작은 원래 요청 수량이 아니라 filledQty/filledAmt 기준으로 사이징하세요.
GET /v2/balance(선택적으로 currencies=btc,eth)는 자산별 객체 배열을 반환합니다. 모든 수량은 문자열입니다:
| 필드 | 의미 |
|---|---|
currency |
자산명, 예: krw, btc |
balance |
총합 = available + tradeInUse + withdrawalInUse |
available |
지금 즉시 거래/출금 가능한 수량 |
tradeInUse |
미체결 주문에 묶인 수량 |
withdrawalInUse |
출금 대기에 묶인 수량 |
avgPrice |
평균 매수가(선택적) |
신규 주문은 balance가 아니라 available 기준으로 사이징하세요 — 그 차이는 이미 미체결 주문이나 출금에 묶여 있습니다.
아래 표는 YAML 스펙의 그룹 순서대로 정리되어 있습니다. 각 엔드포인트의 필수 파라미터와 응답 스키마는 본 문서 하단에 링크된 그룹별 레퍼런스 파일에서 확인하세요.
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
GET |
/v2/tickers |
(공개) | 하나 또는 여러 거래쌍의 현재가(Ticker) 정보를 조회합니다. |
GET |
/v2/orderbook |
(공개) | 단일 거래쌍의 호가 정보를 조회합니다. |
GET |
/v2/trades |
(공개) | 최근 체결 내역을 조회합니다. |
GET |
/v2/candles |
(공개) | 시세 캔들스틱 정보를 조회합니다. |
GET |
/v2/currencyPairs |
(공개) | 거래지원 거래쌍 목록을 각 거래쌍의 통화 구성 및 주문금액 한도와 함께 조회합니다. |
GET |
/v2/tickSizePolicy |
(공개) | 지정한 거래쌍의 호가 정책 및 오더북 모아보기 단위를 조회합니다. |
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
GET |
/v2/orders |
readOrders |
주문 ID(orderId) 또는 사용자 지정 주문 ID(clientOrderId)를 이용해 개별 주문 정보를 조회합니다. |
GET |
/v2/openOrders |
readOrders |
단일 거래쌍의 미체결 주문 목록을 조회합니다. |
GET |
/v2/allOrders |
readOrders |
단일 거래쌍의 최근 주문 목록을 조회합니다. 주문 생성 시각 기준으로 최근 36시간의 주문 내역만 조회 가능합니다. |
GET |
/v2/myTrades |
readOrders |
단일 거래쌍의 최근 체결 목록을 조회합니다. 최근 36시간의 체결 내역만 조회 가능합니다. |
POST |
/v2/orders |
writeOrders |
신규 주문을 생성합니다. |
DELETE |
/v2/orders |
writeOrders |
주문 취소 요청을 전송합니다. |
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
GET |
/v2/balance |
readBalances |
내가 가지고 있는 자산 목록을 조회합니다. |
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
GET |
/v2/coin/depositAddresses |
readDeposits |
가상자산 입금 주소 목록을 조회합니다. |
GET |
/v2/coin/depositAddress |
readDeposits |
개별 가상자산의 입금 주소를 조회합니다. |
POST |
/v2/coin/depositAddress |
writeDeposits |
가상자산을 입금할 주소를 발급합니다. 입금 주소가 이미 존재한다면 신규 발급 없이 기존 입금 주소를 응답합니다. |
GET |
/v2/coin/recentDeposits |
readDeposits |
최근 가상자산 입금 내역을 조회합니다. |
GET |
/v2/coin/deposit |
readDeposits |
가상자산 입금 진행 상태를 조회합니다. |
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
GET |
/v2/coin/withdrawableAddresses |
readWithdrawals |
API 출금 가능 주소로 등록된 주소를 조회합니다. |
GET |
/v2/coin/withdrawableAmount |
readWithdrawals |
출금 가능 수량을 조회합니다. |
POST |
/v2/coin/withdrawal |
writeWithdrawals |
가상자산 출금을 요청합니다. 출금 API를 사용하기 위해서는, 코빗 개발자센터에서 API 출금 허용주소 등록이 필요합니다. |
DELETE |
/v2/coin/withdrawal |
writeWithdrawals |
가상자산 출금을 취소합니다. |
GET |
/v2/coin/recentWithdrawals |
readWithdrawals |
최근 가상자산 출금내역을 조회합니다. |
GET |
/v2/coin/withdrawal |
readWithdrawals |
요청한 출금의 진행 상황을 조회합니다. |
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
POST |
/v2/krw/sendKrwDepositPush |
writeDeposits |
코빗 모바일 앱으로 원화 입금 요청 알림을 전송합니다. |
POST |
/v2/krw/sendKrwWithdrawalPush |
writeWithdrawals |
코빗 모바일 앱으로 원화 출금 요청 알림을 전송합니다. |
GET |
/v2/krw/recentDeposits |
readDeposits |
최근 KRW 입금 내역을 조회합니다. |
GET |
/v2/krw/recentWithdrawals |
readWithdrawals |
최근 KRW 출금내역을 조회합니다. |
| Method | 경로 | 권한 | 용도 |
|---|---|---|---|
GET |
/v2/currencies |
(공개) | 가상자산 정보를 조회합니다. |
GET |
/v2/time |
(공개) | 서버 시각을 조회합니다. |
GET |
/v2/tradingFeePolicy |
readOrders |
현재 회원 계정에 적용되는 거래수수료율을 조회합니다. |
GET |
/v2/currentKeyInfo |
서명 (권한 무관) | 현재 사용중인 API 키의 정보를 조회합니다. |
GET |
/v2/notices |
(공개) | 최근 공지사항 20건을 최신순으로 조회합니다. |
GET |
/v2/marketAlerts |
(공개) | 거래쌍별 시장경보제 발동현황을 조회합니다. 현재 경보가 발동된 거래쌍만 반환됩니다. |
| 값 | 설명 |
|---|---|
pending |
주문 접수 대기 중. 잔고가 부족하거나 timeInForce 조건에 해당할 경우, 주문이 실패해 expired 상태로 될 수 있습니다. |
open |
전량 미체결 |
filled |
체결 완료 후 주문 종료. 미체결 잔량이 오더북에 남지 않고 반환되는 주문(예: ioc 주문, 가격 보호(pp) 범위로 잔량이 잘린 주문)은 주문 수량보다 적게 체결된 경우에도 filled로 종료됩니다. 실제 체결 수량은 filledQty/filledAmt로 확인하세요. |
canceled |
전량 취소 |
partiallyFilled |
부분 체결 |
partiallyFilledCanceled |
부분 체결 후 잔량 취소 |
expired |
주문 접수 실패 (잔고 부족 또는 timeInForce 조건 해당 시) |
| 오류 코드 | 발생 상황 | 경로 |
|---|---|---|
BAD_REQUEST |
잘못된 입력입니다. | POST /v2/orders |
CANNOT_CANCEL_WITHDRAWAL |
출금 취소가 불가능합니다. (출금 처리가 이미 시작된 경우 등) | DELETE /v2/coin/withdrawal |
DAILY_LIMIT_EXCEEDED |
일일 출금 한도를 초과했습니다. | POST /v2/coin/withdrawal |
DUPLICATE_CLIENT_ORDER_ID |
중복된 clientOrderId입니다. | POST /v2/orders |
FORBIDDEN_WITHDRAWAL_ADDRESS |
코빗 정책에 따라 출금이 불가능한 주소입니다. | POST /v2/coin/withdrawal |
INVALID_CURRENCY |
잘못된 가상자산 심볼입니다. | POST /v2/coin/withdrawal |
INVALID_CURRENCY_PAIR |
잘못된 거래쌍입니다. | POST /v2/orders |
INVALID_USER_STATUS |
거래가 제한된 계정입니다. 코빗 웹사이트 또는 고객센터를 통해 상태를 확인하세요. | POST /v2/coin/withdrawal, POST /v2/orders |
NOT_FOUND |
출금 정보가 존재하지 않습니다. | DELETE /v2/coin/withdrawal |
NO_BALANCE |
잔고가 부족합니다. | POST /v2/coin/withdrawal, POST /v2/orders |
ONLY_SELL_LIMIT_ORDERS_ALLOWED |
거래지원 직후에는 지정가 매도 주문만 가능합니다. | POST /v2/orders |
ORDER_ALREADY_CANCELED |
지정한 주문이 이미 취소되었습니다. | DELETE /v2/orders |
ORDER_ALREADY_EXPIRED |
지정한 주문은 실패 처리된 주문입니다. | DELETE /v2/orders |
ORDER_ALREADY_FILLED |
지정한 주문이 이미 전량 체결되었습니다. | DELETE /v2/orders |
ORDER_NOT_FOUND |
지정한 주문 ID가 존재하지 않습니다. | DELETE /v2/orders |
ORDER_VALUE_TOO_LARGE |
주문 최대 금액 초과입니다. 주문금액 한도는 마켓별로 정해지며 해당 거래쌍의 상대자산 단위입니다. GET /v2/currencyPairs의 maxOrderValue, quoteCurrency를 참고하세요. 수량*가격(또는 amt)을 한도 이하로 변경해 주세요. |
POST /v2/orders |
ORDER_VALUE_TOO_SMALL |
주문 최소 금액 미달입니다. 주문금액 한도는 마켓별로 정해지며 해당 거래쌍의 상대자산 단위입니다. GET /v2/currencyPairs의 minOrderValue, quoteCurrency를 참고하세요. 수량*가격(또는 amt)을 한도 이상으로 변경해 주세요. |
POST /v2/orders |
PRICE_OVER_UPPER_BOUND |
거래지원 직후에는 특정한 가격 초과의 호가를 입력할 수 없습니다. | POST /v2/orders |
PRICE_TICK_SIZE_INVALID |
지정가 입력 단위가 올바르지 않습니다. | POST /v2/orders |
PRICE_UNDER_LOWER_BOUND |
거래지원 직후에는 특정한 가격 미만의 호가를 입력할 수 없습니다. | POST /v2/orders |
TOO_MANY_OPEN_ORDERS |
미체결 주문 개수 제한을 초과했습니다. | POST /v2/orders |
TRY_AGAIN |
주문 접수 처리중입니다. 잠시 후 다시 시도해 주세요. | DELETE /v2/orders |
UNREGISTERED_WITHDRAWAL_ADDRESS |
Open API 출금 허용 주소로 등록되어 있지 않은 주소입니다. | POST /v2/coin/withdrawal |
WITHDRAWAL_ALREADY_FINISHED |
출금이 이미 완료되었습니다. | DELETE /v2/coin/withdrawal |
WITHDRAWAL_ALREADY_IN_PROGRESS |
이미 출금 진행 중입니다. 다른 출금 건이 완료된 후 다시 시도해 주세요. | POST /v2/coin/withdrawal |
WITHDRAWAL_SUSPENDED |
출금이 중단된 상태입니다. | POST /v2/coin/withdrawal |