This guide explains how to authenticate your requests when using Upbit’s REST API and WebSocket.
Exchange REST APIs and WebSocket subscription for trading and asset management require API Key-based authentication. This guide provides details on the authentication method and includes implementation examples in various programming languages.
- Singapore (sg): https://sg-api.upbit.com
- Indonesia (id): https://id-api.upbit.com
- Thailand (th): https://th-api.upbit.com
API Key
API Key Issuance
Before starting auth implementation, please refer to the API Key Issuance Guide to issue an API Key and register the caller’s IP addresses in the allowlist. Up to 10 IP addresses can be registered per API Key.
An API Key consists of an Access Key and a Secret Key pair. The Secret Key can only be viewed at the time of issuance. Make sure to keep your Secret Key safe and never expose it to external parties. When generating authentication tokens, you must use the Secret Key that corresponds to the Access Key.
API Key Permission Groups
Upbit API Keys can be configured with specific permissions based on your intended use. If you encounter a permission error while making API calls, please verify that the necessary permissions are assigned to your API Key. For details on each permission group and the supported features, click the box below or refer to the “API Key Permission” section at the bottom of each API Reference page.
Permission Group | Allowed REST APIs | Allowed WebSocket Types |
---|---|---|
- |
|
- |
View Account |
|
Subscribe to MyAsset WebSocket Data |
Make Orders |
|
- |
View Orders |
|
Subscribe to MyOrder WebSocket Data |
Withdraw |
|
- |
View Withrawals |
|
- |
Deposit |
|
- |
View Deposits |
|
- |
Auth Token
An auth token is a string passed with your API requests to verify your identity and access permissions. Upbit APIs use JWT tokens for authentication. When calling auth required private APIs, you must generate a valid JWT token prior to sending the request and include the token in the request header.
Structure of a JWT Token
A JWT consists of three parts: the header, the payload, and the signature. Each part is separated by a dot (.); the header contains information about the algorithm used to generate the signature, the payload contains the body of the content to be verified, and the signature contains the signature value for the payload.
Header
The header uses a Base64-encoded JSON string with the following structure. The alg
field must specify the algorithm used to create the token signature, and HS512
(HMAC with SHA-512) is recommended.
{
"alg": "HS512",
"typ": "JWT"
}
Payload
The payload uses a Base64-encoded JSON string with the following structure.
Key | Description | Required / Optional |
---|---|---|
access_key |
The Access Key of the API Key. Used to specify the API Key. | Required |
nonce |
A random UUID string. To ensure the token value changes with each request even if the same request is repeated, a new value must be provided for every request. | Required |
query_hash |
The hash value of the query string from the request's query parameters or body. | Required if the API request contains query parameters or a body |
query_hash_alg |
The hash algorithm used to generate the query_hash . Not required if query_hash is not present. The default is SHA512 , but if another algorithm was used, it must be specified. |
Optional |
// Example Payload for generating an auth token for REST APIs with parameters or body
{
"access_key": "a7Xd92LmQW3vBtRzYpMj5CxNKeT1HuVs0fFgJcAw",
"nonce": "b2f1e3f8-2dc1-4d6f-a838-c74c49b0e39a",
"query_hash": "0b3e884d40cc992a85730cf470b4a3286f13d9c46204279ef32153bcdcd4edb7c12925e7266636e86a6d6ae5804a6bb394e632e4dba9b4045ad470c93720e5e6",
"query_hash_alg": "SHA512"
}
// Example Payload for generating an auth token for WebSocket and REST APIs without parameters or body
{
"access_key": "a7Xd92LmQW3vBtRzYpMj5CxNKeT1HuVs0fFgJcAw",
"nonce": "b2f1e3f8-2dc1-4d6f-a838-c74c49b0e39a"
}
Signature
The Secret Key of the API Key is used to sign the header and payload. When implementing this in code, it is recommended to use JWT libraries available for each programming language to generate the token.
JWT Generation Guide – How to Generate the query_hash
in the Payload
query_hash
in the PayloadThe query_hash
value in the payload is a hash of the query string or the JSON body formatted as a query string. If the actual request content or the order of parameters differs from what is included in the authentication token, token validation will fail. Please review the following guidelines carefully:
GET
or DELETE
REST API Requests
- The
query_hash
must be calculated from the actual query string included in the request, without changing the parameter order or re-sorting any elements. - When using multiple parameters for array-type query parameters whose names explicitly include [] (e.g., uuids[] or states[]), the query string must be constructed by repeating the key-value pairs, such as uuids[]=UUID1&uuids[]=UUID2...
- For parameters that do not use brackets (e.g., pairs, quote_currencies) but support multiple values separated by commas, construct the query string as
pairs=SGD-BTC,SGD-ETH...
- The hash must be generated using the
non-URL-encoded
query string.
GET
request to /v1/orders/open?market=SGD-BTC&limit=10, the query string is market=SGD-BTC&limit=10
. The query_hash
value is the hash of market=SGD-BTC&limit=10
.GET
request to /v1/orders/open?market=SGD-BTC&states[]=wait&states[]=watch, the query string is market=SGD-BTC&states[]=wait&states[]=watch
. The query_hash
value is the hash of market=SGD-BTC&states[]=wait&states[]=watch
.POST
REST API Requests
- For JSON-formatted request bodies, convert all key-value pairs into a query string format by connecting keys and values with
=
and separating pairs with&
.
For the following request body:
{
"market":"SGD-BTC",
"side":"bid",
"volume":"0.01",
"price":"100.0",
"ord_type":"limit"
}
Generate the query string:
market=SGD-BTC&side=bid&volume=0.01&price=100.0&ord_type=limit
and then create the hash value from this string.How to Send a JWT Authentication Token
The generated JWT authentication token must be included in the Authorization header of your request. This applies to both REST API calls and WebSocket connection requests. Use the Bearer token format as shown below:
- Key:
Authorization
- Value: Bearer {Insert the generated JWT token after the space}
JWT Token Generation Guide — Code Examples
Below are example code snippets in various programming languages for generating a JWT token.
REST API Request Example
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:
"""
Converts dictionary parameters into a query string format.
"""
return unquote(urlencode(params, doseq=True))
def _create_jwt(access_key: str, secret_key: str, query_string: str = "") -> str:
"""
Generates a JWT token.
"""
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://sg-api.upbit.com"
access_key = "YOUR_ACCESS_KEY"
secret_key = "YOUR_SECRET_KEY" # Make sure to load or inject this securely.
# Example request without parameters
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())
# Example GET request with parameters
params = {
"market": "SGD-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())
# Example POST request
order_data = {
"market": "SGD-BTC",
"side": "bid",
"volume": "0.001",
"price": "5000000",
"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"
}
# Uncomment below to place an actual order; verify carefully before execution.
# 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();
/**
* Generates SHA-512 hash of the input string.
*/
public static String sha512(String input) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(input.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(md.digest());
}
/**
* Creates JWT token using Access Key, Secret Key, and optional Query String.
*/
private static String createJwt(String accessKey, String secretKey, String queryString)
throws NoSuchAlgorithmException {
byte[] secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
Algorithm algorithm;
try {
algorithm = Algorithm.HMAC512(secretKeyBytes);
} finally {
Arrays.fill(secretKeyBytes, (byte) 0);
}
JWTCreator.Builder builder = JWT.create()
.withHeader(Collections.singletonMap("alg", "HS512"))
.withClaim("access_key", accessKey)
.withClaim("nonce", UUID.randomUUID().toString());
if (queryString != null && !queryString.isEmpty()) {
String queryHash = sha512(queryString);
builder.withClaim("query_hash", queryHash);
builder.withClaim("query_hash_alg", "SHA512");
}
return builder.sign(algorithm);
}
/**
* Converts JSON-formatted request body to query string.
*/
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()) {
}
return "";
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")
.replace("%5B", "[").replace("%5D", "]");
String encodedValue = URLEncoder.encode(String.valueOf(entry.getValue()), "UTF-8");
queryElements.add(encodedKey + "=" + encodedValue);
} catch (Exception e) {
throw new RuntimeException("Encoding failed", e);
}
}
}
return String.join("&", queryElements);
}
/**
* Builds URL-encoded query string from Map<String, Object> parameters.
*/
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://sg-api.upbit.com";
String accessKey = "YOUR_ACCESS_KEY";
String secretKey = "YOUR_SECRET_KEY"; // Load or inject securely in production
OkHttpClient client = new OkHttpClient();
// Example GET request with parameters
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());
// Example POST request
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String jsonBody = "{\"market\":\"SGD-BTC\",\"side\":\"bid\",\"volume\":\"0.0001\",\"price\":\"500000\",\"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();
// Uncomment the following lines to place an actual order. Please verify before executing.
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');
// Required credentials
const ACCESS_KEY = 'YOUR_ACCESS_KEY';
const SECRET_KEY = 'YOUR_SECRET_KEY'; // Manage securely
const BASE_URL = 'https://sg-api.upbit.com';
/**
* SHA512 hash function
*/
function sha512(text) {
return crypto.createHash('sha512').update(text, 'utf8').digest('hex');
}
/**
* JWT token creation
*/
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 request example
*/
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 request example
*/
async function placeOrder() {
const body = {
market: 'SGD-BTC',
side: 'bid',
volume: '0.001',
price: '500000',
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 {
// Uncomment the following to place an actual order. Confirm carefully before running.
// 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);
}
}
// Main execution
(async () => {
await getOpenOrders();
await placeOrder();
})();
Websocket Connection Request Example
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://sg-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 {
/**
* Example method to create a JWT Token for WebSocket authentication.
*/
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) {
// Implement onOpen event
// TODO: Send subscription or data request here
}
@Override
public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) {
// Implement onMessage event - handle incoming data
}
@Override
public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
// Implement onClosing event
}
@Override
public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, Response response) {
// Implement onFailure event - handle errors
}
};
}
public static void main(String[] args) {
String accessKey = "YOUR_ACCESS_KEY";
String secretKey = "YOUR_SECRET_KEY"; // Load or inject securely in production
OkHttpClient httpClient = new OkHttpClient();
try {
// Generate JWT token for authentication
String jwtToken = createJwt(accessKey, secretKey);
// Build WebSocket request with Authorization header
Request request = new Request.Builder()
.url("wss://sg-api.upbit.com/websocket/v1/private")
.addHeader("Authorization", "Bearer " + jwtToken)
.build();
// Open WebSocket connection
WebSocket webSocket = httpClient.newWebSocket(request, createWebSocketListener());
// Keep the program running or implement additional logic as needed
} 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://sg-api.upbit.com/websocket/v1/private", {
headers: {
authorization: `Bearer ${jwtToken}`
},
});
ws.on("open", () => {
console.log("connected!");
// After connection, send subscription request
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!"));