REST API 및 WebSocket 사용을 위한 인증 가이드입니다.
거래 및 자산 관리(Exchange) REST API를 호출하거나 WebSocket 데이터를 수신하려면 API Key 기반의 인증이 반드시 필요합니다. 본 가이드를 통해 인증 방식을 확인하고 언어별 구현 예시를 참고 할 수 있습니다.
API Key
API Key 발급 및 구조 안내
인증 구현에 앞서 API Key 발급 가이드를 참고해 API Key를 발급받고, 호출지의 IP를 허용 목록에 등록해야 합니다. API Key 당 최대 10개의 IP를 등록할 수 있습니다.
API Key는 Access Key와 Secret Key 쌍으로 구성되며, Secret Key는 발급 시에만 확인 가능합니다. Secret Key는 외부에 노출되지 않도록 반드시 안전하게 보관해야 하며, 토큰 생성 시 반드시 Access Key와 짝을 이루는 Secret Key를 사용해야 합니다.
API Key의 권한 그룹
업비트 API Key는 발급 시 사용 용도에 따라 필요한 권한만 선택적으로 부여할 수 있습니다. API 호출 시 권한 오류가 발생했다면, 해당 API Key에 필요한 권한이 포함되어 있는지 확인하세요. 각 권한 그룹이 지원하는 기능 목록은 아래 박스를 클릭해 확인하거나, API Reference 문서 하단의 “API Key Permission” 항목을 참고하시기 바랍니다.
권한 그룹 | 허용 REST API | 허용 WebSocket 타입 |
---|---|---|
- |
|
- |
자산조회 |
|
내 자산(MyAsset) 타입 데이터 구독 |
주문하기 |
|
- |
주문조회 |
|
내 주문 및 체결(MyOrder) 타입 데이터 구독 |
출금하기 |
|
- |
출금조회 |
|
- |
입금하기 |
|
- |
입금 조회 |
|
- |
인증 토큰
인증 토큰이란 서버에 요청을 보낼 때 사용자의 신원 및 권한을 증명하기 위해 전달하는 문자열 정보입니다. 업비트 API는 JWT 토큰 기반 인증을 사용합니다. 인증이 필요한 API를 요청하는 경우 요청을 전송하기 전에 유효한 JWT 토큰을 생성하고, 요청 헤더에 생성된 토큰을 포함하여 전송해야 합니다.
JWT 토큰 구조
JWT 토큰은 헤더(Header), 페이로드(Payload), 서명(Signature)의 세 부분으로 구성됩니다. 각 부분은 온점(.)으로 구분되며, 헤더에는 JWT 서명 생성시 사용한 알고리즘 정보가, 페이로드에는 검증하고자 하는 내용 본문이, 서명에는 페이로드에 대한 서명 값이 포함됩니다.
헤더(Header)
헤더는 다음과 같은 구조의 JSON 데이터를 Base64 인코딩한 결과 문자열을 사용합니다. 이때, alg
필드에는 실제 토큰 서명 생성에 사용한 알고리즘을 명시해야 하며, HS512
(HMAC with SHA-512) 사용을 권장합니다.
{
"alg": "HS512",
"typ": "JWT"
}
페이로드(Payload)
페이로드는 다음과 같은 구조의 JSON 데이터를 Base64 인코딩한 결과 문자열을 사용합니다.
Key | 설명 | 필수 여부(필수/선택) |
---|---|---|
access_key |
API Key의 Access key. API Key를 지정하기 위해 사용합니다. | 필수 |
nonce |
무작위의 UUID 문자열. 동일한 요청을 반복하더라도 항상 토큰 값이 변경되도록, 매 요청마다 새로운 값을 입력해야 합니다. | 필수 |
query_hash |
요청 쿼리 파라미터 또는 본문(body)의 쿼리 스트링(Query String) Hash값. | REST API 요청에 쿼리 파라미터 또는 본문이 있는 경우 필수 |
query_hash_alg |
query_hash_alg 생성시 사용한 Hash 알고리즘. query_hash 값이 없는 경우 입력하지 않습니다. 기본값은 SHA512 로, query_hash 생성시 다른 알고리즘을 사용한 경우 필수로 입력해야 합니다. |
선택 |
// 파라미터 또는 본문(Body)이 있는 REST API 인증 토큰 생성을 위한 Payload 예시
{
"access_key": "a7Xd92LmQW3vBtRzYpMj5CxNKeT1HuVs0fFgJcAw",
"nonce": "b2f1e3f8-2dc1-4d6f-a838-c74c49b0e39a",
"query_hash": "0b3e884d40cc992a85730cf470b4a3286f13d9c46204279ef32153bcdcd4edb7c12925e7266636e86a6d6ae5804a6bb394e632e4dba9b4045ad470c93720e5e6",
"query_hash_alg": "SHA512"
}
// WebSocket 및 파라미터 또는 본문이 없는 REST API인증 토큰 생성을 위한 Payload 예시
{
"access_key": "a7Xd92LmQW3vBtRzYpMj5CxNKeT1HuVs0fFgJcAw",
"nonce": "b2f1e3f8-2dc1-4d6f-a838-c74c49b0e39a"
}
서명(Signature)
API Key의 Secret Key를 사용하여 헤더와 페이로드를 서명합니다. 코드로 구현하는 경우 언어별로 지원되는 JWT 라이브러리를 활용하여 생성하는 것을 권장합니다.
JWT 토큰 생성 가이드 - 페이로드의 query_hash
값 생성 안내
query_hash
값 생성 안내페이로드의 query_hash 값은 쿼리 문자열 또는 쿼리 문자열 형태로 가공한 JSON 본문(Body)을 Hash한 값입니다. 실제 요청 내용과 인증 토큰에 포함된 내용 및 문자열 구성 순서가 다른 경우 토큰 검증 오류가 발생할 수 있으므로 아래 주의사항을 반드시 확인한 뒤 사용하시기 바랍니다.
GET
또는 DELETE
REST API 요청 시
- 실제 요청에 포함된 쿼리 문자열을 그대로 발췌하여 Hash합니다. 파라미터간 순서를 변경하거나 재정렬하지 않습니다.
- 'uuids[]' 또는 'states[]와' 같이 이름에 []가 명시된 배열 형식의 쿼리 파라미터에 다중 파라미터를 사용하는 경우 반드시 'uuids[]=UUID1&uuids[]=UUID2...'와 같이 Key-Value를 반복하여 연결하는 형식으로 쿼리 문자열을 구성해야 합니다.
pairs
,quote_currencies
와 같이 이름에 []가 명시되어 있지 않고 쉼표(,)로 구분되는 문자열 형식을 지원하는 파라미터의 경우 쿼리 문자열을pairs=KRW-BTC,KRW-ETH...
와 같이 구성합니다.- URL 인코딩 되지 않은 쿼리 문자열을 기준으로 Hash 값을 생성해야 합니다.
GET
/v1/orders/open?market=KRW-BTC&limit=10 로 요청 시 쿼리 문자열은 market=KRW-BTC&limit=10
가 됩니다. query_hash
값은 market=KRW-BTC&limit=10
을 Hash 한 값을 사용합니다.GET
/v1/orders/open?market=KRW-BTC&states[]=wait&states[]=watch 로 요청 시 쿼리 문자열은 market=KRW-BTC&states[]=wait&states[]=watch
가 됩니다. query_hash
값은 market=KRW-BTC&states[]=wait&states[]=watch
을 Hash 한 값을 사용합니다.POST
REST API 요청 시
- JSON 형식의 요청 본문(Body)의 모든 Key-Value 쌍을 쿼리 문자열 형식(
=
로 Key-Value 연결,&
로 구분)으로 가공해야 합니다.
아래와 같은 본문에 대해
{
  "market":"KRW-BTC",
  "side":"bid",
  "volume":"0.01",
  "price":"100.0",
  "ord_type":"limit"
}
market=KRW-BTC&side=bid&volume=0.01&price=100.0&ord_type=limit
로 쿼리 문자열을 생성한 후 Hash 값을 생성합니다.인증 토큰 전송 방식
생성한 JWT 인증 토큰은 요청의 Authorization 헤더를 이용하여 전송합니다. REST API 호출과 WebSocket 연결 요청에 동일하게 적용됩니다. Bearer 방식으로 처리하기 위해 다음과 같이 요청합니다.
- Key:
Authorization
- Value: Bearer {공백 뒤 생성한 JWT 토큰 값을 입력}
JWT 토큰 생성 가이드 - 코드 예시
주요 언어별 JWT 토큰 생성 코드 예시는 아래와 같습니다.
REST API 호출 예시
from urllib.parse import quote, unquote, urlencode
from typing import Any, Dict
import hashlib
import uuid
import jwt # PyJWT
import requests
def _build_query_string(params: Dict[str, Any]) -> str:
return unquote(urlencode(params, doseq=True))
def _create_jwt(access_key: str, secret_key: str, query_string: str = "") -> str:
payload = {"access_key": access_key, "nonce": str(uuid.uuid4())}
if query_string:
query_hash = hashlib.sha512(query_string.encode("utf-8")).hexdigest()
payload["query_hash"] = query_hash
payload["query_hash_alg"] = "SHA512"
token = jwt.encode(payload, secret_key, algorithm="HS512")
return token if isinstance(token, str) else token.decode('utf-8')
if __name__ == "__main__":
base_url = "https://api.upbit.com"
access_key = "YOUR_ACCESS_KEY"
secret_key = "YOUR_SECRET_KEY" # 실제로는 안전한 방식으로 로드하거나 주입하세요.
# 파라미터가 없는 요청 예시
jwt_token = _create_jwt(access_key, secret_key)
headers = {"Authorization": f"Bearer {jwt_token}"}
response = requests.get(f"{base_url}/v1/accounts", headers=headers)
print(response.json())
# 파라미터가 있는 GET 요청 예시
params = {
"market": "KRW-BTC",
"states[]": ["wait", "watch"],
"limit": 10
}
query_string = _build_query_string(params)
jwt_token = _create_jwt(access_key, secret_key, query_string)
headers = {"Authorization": f"Bearer {jwt_token}"}
response = requests.get(f"{base_url}/v1/orders/open?{query_string}", headers=headers)
print(response.json())
# POST 요청 예시
order_data = {
"market": "KRW-BTC",
"side": "bid",
"volume": "0.001",
"price": "50000000",
"ord_type": "limit"
}
query_string = _build_query_string(order_data)
jwt_token = _create_jwt(access_key, secret_key, query_string)
headers = {
"Authorization": f"Bearer {jwt_token}",
"Content-Type": "application/json"
}
# 아래 주석처리된 부분 실행시 실제 주문이 발생하므로 실행 전 반드시 확인하세요.
# response = requests.post(f"{base_url}/v1/orders", json=order_data, headers=headers)
# print(response.json())
package com.upbit.openapi.test;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.google.gson.Gson;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class Auth {
private static Gson gson = new Gson();
/**
* 문자열을 입력 받아 Hash를 생성하는 예제
*/
public static String sha512(String input) throws NoSuchAlgorithmException {
// input은 non-null의 유효한 값으로 가정
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(input.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(md.digest());
}
/**
* Access Key, Secret Key, QueryString을 입력받아 JWT Token을 생성하는 예제
*/
private static String createJwt(String accessKey, String secretKey, String queryString)
throws NoSuchAlgorithmException {
// accessKey, secretKey, payload는 모두 non-null의 유효한 값으로 가정
byte[] secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
Algorithm algorithm;
try {
algorithm = Algorithm.HMAC512(secretKeyBytes);
} finally {
Arrays.fill(secretKeyBytes, (byte) 0);
}
// Build JWT with claims
JWTCreator.Builder builder = JWT.create()
.withHeader(Collections.singletonMap("alg", "HS512"))
.withClaim("access_key", accessKey)
.withClaim("nonce", UUID.randomUUID().toString());
// queryString이 있는 경우 페이로드에 Hash 추가
if (queryString != null && !queryString.isEmpty()) {
String queryHash = sha512(queryString);
builder.withClaim("query_hash", queryHash);
builder.withClaim("query_hash_alg", "SHA512");
}
return builder.sign(algorithm);
}
/**
* 요청의 Json Body를 QueryString으로 변환하는 예제
*/
public static String jsonToQueryString(String jsonString) {
if (jsonString == null || jsonString.isEmpty()) {
return "";
}
Map<String, Object> bodyMap = gson.fromJson(jsonString, Map.class);
if (bodyMap != null && !bodyMap.isEmpty()) {
List<String> queryElements = new ArrayList<>();
for (Map.Entry<String, Object> entry : bodyMap.entrySet()) {
if (entry.getValue() != null) {
try {
String encodedKey = URLEncoder.encode(entry.getKey(), "UTF-8");
String encodedValue = URLEncoder.encode(String.valueOf(entry.getValue()),
"UTF-8");
encodedKey = encodedKey.replace("%5B", "[").replace("%5D", "]");
queryElements.add(encodedKey + "=" + encodedValue);
} catch (Exception e) {
throw new RuntimeException("Encoding failed", e);
}
}
}
return String.join("&", queryElements);
}
return "";
}
/**
* Map<String, Object>를 URL 인코딩된 Query String 변환하는 예제
*/
public static String buildQueryString(Map<String, Object> params) {
if (params == null || params.isEmpty()) {
return "";
}
List<String> components = new ArrayList<>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value == null) {
continue;
}
List<Object> values;
if (value instanceof List) {
values = (List<Object>) value;
} else {
values = Collections.singletonList(value);
}
for (Object val : values) {
try {
String encodedKey = URLEncoder.encode(
key.endsWith("[]") ? key : key + "[]", StandardCharsets.UTF_8
).replace("%5B", "[").replace("%5D", "]");
String encodedVal = URLEncoder.encode(String.valueOf(val),
StandardCharsets.UTF_8);
components.add(encodedKey + "=" + encodedVal);
} catch (Exception e) {
throw new RuntimeException("Encoding failed", e);
}
}
}
return String.join("&", components);
}
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
String baseUrl = "https://api.upbit.com";
String accessKey = "YOUR_ACCESS_KEY";
String secretKey = "YOUR_SECRET_KEY"; // 실제로는 안전하게 로드하거나 주입하세요.
OkHttpClient client = new OkHttpClient();
// 파라미터가 있는 GET 요청 예시
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("states[]", Arrays.asList("wait", "watch"));
queryParams.put("limit", 100);
String queryString = buildQueryString(queryParams);
String jwtTokenGet = createJwt(accessKey, secretKey, queryString);
Request getRequest = new Request.Builder()
.url(baseUrl + "/v1/orders/open?" + queryString)
.get()
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer " + jwtTokenGet)
.build();
Response response = client.newCall(getRequest).execute();
System.out.println(response.code());
System.out.println(Objects.requireNonNull(response.body()).string());
// POST 요청 예시
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String jsonBody = "{\"market\":\"KRW-BTC\",\"side\":\"bid\",\"volume\":\"0.0001\",\"price\":\"50000000\",\"ord_type\":\"limit\"}";
String queryStringBody = jsonToQueryString(jsonBody);
String jwtTokenPost = createJwt(accessKey, secretKey, queryStringBody);
Request postRequest = new Request.Builder()
.url(baseUrl + "/v1/orders")
.post(RequestBody.create(jsonBody, JSON))
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer " + jwtTokenPost)
.build();
// 아래 주석처리된 부분 실행시 실제 주문이 발생하므로 실행 전 반드시 확인하세요.
response = client.newCall(postRequest).execute();
System.out.println(response.code());
System.out.println(Objects.requireNonNull(response.body()).string());
}
}
const axios = require('axios');
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
const qs = require('querystring');
// 필수 정보 입력
const ACCESS_KEY = 'YOUR_ACCESS_KEY';
const SECRET_KEY = 'YOUR_SECRET_KEY'; // 안전하게 관리하세요
const BASE_URL = 'https://api.upbit.com';
/**
* SHA512 해시 함수
*/
function sha512(text) {
return crypto.createHash('sha512').update(text, 'utf8').digest('hex');
}
/**
* JWT 토큰 생성
*/
function createJwt(accessKey, secretKey, queryString = '') {
const payload = {
access_key: accessKey,
nonce: uuidv4(),
};
if (queryString) {
payload.query_hash = sha512(queryString);
payload.query_hash_alg = 'SHA512';
}
return jwt.sign(payload, secretKey, { algorithm: 'HS512' });
}
/**
* GET 요청 예제
*/
async function getOpenOrders() {
const query = {
states: ['wait', 'watch'],
page: 1,
limit: 10,
};
const queryString = qs.stringify(query, '&', '=', {
encodeURIComponent: (str) =>
str.endsWith('[]') ? encodeURIComponent(str).replace('%5B%5D', '[]') : encodeURIComponent(str),
});
const token = createJwt(ACCESS_KEY, SECRET_KEY, queryString);
const headers = {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
};
try {
const res = await axios.get(`${BASE_URL}/v1/orders/open?${queryString}`, { headers });
console.log('[GET] Status:', res.status);
console.log(res.data);
} catch (err) {
console.error('[GET] Error:', err.response?.data || err.message);
}
}
/**
* POST 요청 예제
*/
async function placeOrder() {
const body = {
market: 'KRW-BTC',
side: 'bid',
volume: '0.001',
price: '50000000',
ord_type: 'limit',
};
const queryString = qs.stringify(body);
const token = createJwt(ACCESS_KEY, SECRET_KEY, queryString);
const headers = {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
};
try {
// 아래 주석처리된 부분 실행시 실제 주문이 발생하므로 실행 전 반드시 확인하세요.
// const res = await axios.post(`${BASE_URL}/v1/orders`, body, { headers });
// console.log('[POST] Status:', res.status);
// console.log(res.data);
console.log('[POST] Request prepared but not sent (order disabled for safety).');
} catch (err) {
console.error('[POST] Error:', err.response?.data || err.message);
}
}
// 메인 실행
(async () => {
await getOpenOrders();
await placeOrder();
})();
WebSocket 연결 요청 예시
import jwt # PyJWT
import uuid
import websocket # websocket-client
def on_message(ws, message):
# do something
data = message.decode('utf-8')
print(data)
def on_connect(ws):
print("connected!")
# Request after connection
ws.send('[{"ticket":"test example"},{"type":"myOrder"}]')
def on_error(ws, err):
print(err)
def on_close(ws, status_code, msg):
print("closed!")
payload = {
'access_key': "YOUR_ACCESS_KEY",
'nonce': str(uuid.uuid4()),
}
jwt_token = jwt.encode(payload, "YOUR_SECRET_KEY");
authorization_token = 'Bearer {}'.format(jwt_token)
headers = {"Authorization": authorization_token}
ws_app = websocket.WebSocketApp("wss://api.upbit.com/websocket/v1/private",
header=headers,
on_message=on_message,
on_open=on_connect,
on_error=on_error,
on_close=on_close)
ws_app.run_forever()
package com.upbit.openapi.test;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.UUID;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
public class AuthWebSocket {
/**
* JWT Token 생성 메소드 예시
*/
private static String createJwt(String accessKey, String secretKey) {
try {
Algorithm algorithm = Algorithm.HMAC256(secretKey);
return JWT.create().withClaim("access_key", accessKey)
.withClaim("nonce", UUID.randomUUID().toString()).sign(algorithm);
} catch (Exception e) {
throw new RuntimeException("JWT token generation failed", e);
}
}
static WebSocketListener createWebSocketListener() {
return new WebSocketListener() {
@Override
public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
// onOpen 이벤트 구현
// TODO: 데이터 요청
}
@Override
public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) {
// onMessage 이벤트 구현
}
@Override
public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
// onClosing 이벤트 구현
}
@Override
public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, Response response) {
// onFailure 이벤트 구현
}
};
}
public static void main(String[] args){
String accessKey = "YOUR_ACCESS_KEY";
String secretKey = "YOUR_SECRET_KEY"; // 실제로는 안전하게 로드하거나 주입하세요.
OkHttpClient httpClient = new OkHttpClient();
try {
// Generate JWT token for authentication
String jwtToken = createJwt(accessKey, secretKey);
// Build request with authentication header
Request request = new Request.Builder()
.url("wss://api.upbit.com/websocket/v1/private")
.addHeader("Authorization", "Bearer " + jwtToken)
.build();
WebSocket webSocket = httpClient.newWebSocket(request, createWebSocketListener());
//
} catch (Exception e) {
throw new RuntimeException("Failed to connect to private WebSocket", e);
}
}
}
const jwt = require("jsonwebtoken");
const {v4: uuidv4} = require('uuid');
const WebSocket = require("ws");
const payload = {
access_key: "YOUR_ACCESS_KEY",
nonce: uuidv4(),
};
const jwtToken = jwt.sign(payload, "YOUR_SECRET_KEY");
const ws = new WebSocket("wss://api.upbit.com/websocket/v1/private", {
headers: {
authorization: `Bearer ${jwtToken}`
}
});
ws.on("open", () => {
console.log("connected!");
ws.send('[{"ticket":"test example"},{"type":"myOrder"}]');
});
ws.on("error", console.error);
ws.on("message", (data) => console.log(data.toString()));
ws.on("close", () => console.log("closed!"));