Authentication

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.

Check the proper endpoint based on your region.

The examples in this page is written using Singapore fiat code(SGD). Set the quote currency to match your region. The base_url differs by country/region. Make sure to specify the correct region value for your environment.


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.

Announcement WebSocket API Key Permission

The Announcement WebSocket uses the Private WebSocket endpoint and requires authentication. However, no separate API Key permission is required.


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.

Example and Structure of a JWT Token

JWT

eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3Nfa2V5IjoiYTdYZDkyTG1RVzN2QnRSellwTWo1Q3hOS2VUMUh1VnMwZkZnSmNBdyIsIm5vbmNlIjoiYjJmMWUzZjgtMmRjMS00ZDZmLWE4MzgtYzc0YzQ5YjBlMzlhIiwicXVlcnlfaGFzaCI6IjBiM2U4ODRkNDBjYzk5MmE4NTczMGNmNDcwYjRhMzI4NmYxM2Q5YzQ2MjA0Mjc5ZWYzMjE1M2JjZGNkNGVkYjdjMTI5MjVlNzI2NjYzNmU4NmE2ZDZhZTU4MDRhNmJiMzk0ZTYzMmU0ZGJhOWI0MDQ1YWQ0NzBjOTM3MjBlNWU2IiwicXVlcnlfaGFzaF9hbGciOiJTSEE1MTIifQ.jFOftW_qV7loHsaeZj_hrpkP2iEgdK62D9rwseXSGbwvlNaLG2-iJ-WBFUEG7c1tch0AA-jsHsonhEhezd8sTw

Header

{
  "alg": "HS512",
  "typ": "JWT"
}

Payload

{
  "access_key": "a7Xd92LmQW3vBtRzYpMj5CxNKeT1HuVs0fFgJcAw",
  "nonce": "b2f1e3f8-2dc1-4d6f-a838-c74c49b0e39a",
  "query_hash": "0b3e884d40cc992a85730cf470b4a3286f13d9c46204279ef32153bcdcd4edb7c12925e7266636e86a6d6ae5804a6bb394e632e4dba9b4045ad470c93720e5e6",
  "query_hash_alg": "SHA512"
}

Signature

HMAC-SHA512(base64UrlEncode(header) + "." + base64UrlEncode(payload), Secret Key)

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.

KeyDescriptionRequired / Optional
access_keyThe Access Key of the API Key. Used to specify the API Key.Required
nonceA 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_hashThe 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_algThe 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.

The Secret Key is not Base64-encoded.

When generating the JWT token signature, there is no need to perform separate Base64 decoding of the key. Please refer to this detail when implementing with JWT libraries.


JWT Generation Guide – How to Generate the query_hash in the Payload

The 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.

Example — When calling the List Open Orders API

  • For a 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.

  • For a 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 &.

Example — When calling the Create Order API

For the request body {"market":"SGD-BTC","side":"bid","volume":"0.01","price":"100.0","ord_type":"limit"}, the query string is market=SGD-BTC&side=bid&volume=0.01&price=100.0&ord_type=limit.

Generate the hash value from market=SGD-BTC&side=bid&volume=0.01&price=100.0&ord_type=limit.


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.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!")
);