인증 토큰 생성하기
빗썸 Private API 호출에 필요한 JWT 인증 토큰의 구조와 생성 규칙을 설명합니다
API Key가 아직 없다면 빠른 시작 가이드 > API Key 발급을 먼저 확인하세요.
인증 흐름
Private API는 요청마다 JWT(JSON Web Token) 인증 토큰을 생성하여 Authorization 헤더에 포함해야 합니다.
- API Key와 Secret Key 준비
- JWT Payload 구성
- Secret Key로 서명 (HS256)
- Authorization 헤더에 Bearer 토큰 포함
- API 요청 전송
GET /v1/accounts HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json; charset=utf-8JWT 구조
빗썸 API의 JWT는 세 부분으로 구성됩니다.
| 구성 | 내용 |
|---|---|
| Header | {"alg": "HS256", "typ": "JWT"} — 서명 알고리즘 |
| Payload | 인증 정보를 담는 클레임(아래 상세) |
| Signature | Secret Key로 생성한 HMAC-SHA256 서명 |
Payload 필드
요청에 파라미터가 있는지 여부에 따라 포함하는 필드가 달라집니다.
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
access_key | string | ✅ 항상 | 발급받은 API Key |
nonce | string | ✅ 항상 | 요청마다 고유한 값(UUID 권장) |
timestamp | integer | ✅ 항상 | 현재 시각(밀리초 단위 Unix timestamp) |
query_hash | string | 파라미터 있을 때 | 쿼리 문자열의 SHA-512 해시값 |
query_hash_alg | string | 파라미터 있을 때 | 해시 알고리즘("SHA512" 고정) |
파라미터가 없는 경우
access_key, nonce, timestamp 3개 필드만 포함합니다.
{
"access_key": "L7rVaYfBIc2BDsnlQGfkR93d6DoOAJCw7mJr5Eso",
"nonce": "6f5570df-d8bc-4daf-85b4-976733feb624",
"timestamp": 1712230310689
}파라미터가 있는 경우
쿼리 문자열을 SHA-512로 해싱하여 query_hash와 query_hash_alg를 추가합니다.
{
"access_key": "L7rVaYfBIc2BDsnlQGfkR93d6DoOAJCw7mJr5Eso",
"nonce": "6f5570df-d8bc-4daf-85b4-976733feb624",
"timestamp": 1712230310689,
"query_hash": "1c2362ca9d79947582cae192acf63efb8756caa49af1eb64b12ba45617165431...",
"query_hash_alg": "SHA512"
}코드 예시
파라미터 없는 요청
GET /v1/accounts(전체 계좌 조회)와 같이 쿼리 파라미터가 없는 요청에 사용합니다.
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios')
const accessKey = '발급받은 API KEY'
const secretKey = '발급받은 SECRET KEY'
const apiUrl = 'https://api.bithumb.com'
// Generate access token
const payload = {
access_key: accessKey,
nonce: uuidv4(),
timestamp: Date.now()
};
const jwtToken = jwt.sign(payload, secretKey)
const config = {
headers: {
Authorization: `Bearer ${jwtToken}`
}
}
// Call API
axios.get(apiUrl + '/v1/accounts', config)
.then((response) => {
// handle to success
console.log('status: ', response.status)
console.log('data: ', response.data)
})
.catch((error) => {
// handle to fail
console.log(error.response.status)
console.log(error.response.data)
});파라미터 있는 요청
쿼리 파라미터가 있는 요청(예: GET /v1/orders/chance?market=KRW-BTC)의 경우, 파라미터를 해싱하여 JWT payload에 포함해야 합니다. 아래는 주문 가능 정보 조회 API 호출 예시입니다.
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
const crypto = require('crypto');
const querystring = require('querystring');
const axios = require('axios')
const accessKey = '발급받은 API KEY'
const secretKey = '발급받은 SECRET KEY'
const apiUrl = 'https://api.bithumb.com'
// Set API parameters
const query = 'market=KRW-BTC'
// Generate access token
const alg = 'SHA512'
const hash = crypto.createHash(alg)
const queryHash = hash.update(query, 'utf-8').digest('hex')
const payload = {
access_key: accessKey,
nonce: uuidv4(),
timestamp: Date.now(),
query_hash: queryHash,
query_hash_alg: alg
}
const jwtToken = jwt.sign(payload, secretKey)
const config = {
headers: {
Authorization: `Bearer ${jwtToken}`
}
}
// Call API
axios.get(apiUrl + '/v1/orders/chance?' + query, config)
.then((response) => {
// handle to success
console.log('status: ', response.status)
console.log('data: ', response.data)
})
.catch((error) => {
// handle to fail
console.log(error.response.status)
console.log(error.response.data)
})query_hash 상세 규칙
query_hash는 GET 쿼리 파라미터와 POST/DELETE body 파라미터 모두에 적용됩니다. 파라미터를 쿼리 문자열 형태로 변환한 뒤 SHA-512로 해싱합니다.
POST 요청 — body 파라미터
body의 JSON 파라미터를 쿼리 문자열 형태로 변환한 뒤 동일하게 해싱합니다.
POST /v2/orders
Body: {"market": "KRW-BTC", "side": "bid", "order_type": "limit", "price": "80000000", "volume": "0.001"}
→ 쿼리 문자열로 변환: "market=KRW-BTC&side=bid&order_type=limit&price=80000000&volume=0.001"
→ 이 문자열을 SHA-512 해싱
const crypto = require('crypto');
const requestBody = {
market: 'KRW-BTC',
side: 'bid',
order_type: 'limit',
price: '80000000',
volume: '0.001',
};
// body 파라미터를 쿼리 문자열로 변환
const query = Object.entries(requestBody)
.map(([key, value]) => `${key}=${value}`)
.join('&');
const hash = crypto.createHash('SHA512');
const queryHash = hash.update(query, 'utf-8').digest('hex');배열 파라미터 처리
파라미터에 배열이 포함된 경우, 쿼리 문자열 형태에 주의해야 합니다. 배열의 각 요소를 key[]=value 형태로 개별 전개합니다.
예를 들어 여러 주문을 조회할 때:
파라미터: { "order_ids": ["id1", "id2", "id3"] }
✅ 올바른 쿼리 문자열:
order_ids[]=id1&order_ids[]=id2&order_ids[]=id3
❌ 잘못된 쿼리 문자열:
order_ids=id1,id2,id3
const crypto = require('crypto');
const querystring = require('querystring');
const params = {
order_ids: ['id1', 'id2', 'id3']
};
// querystring.stringify는 배열을 자동으로 key=v1&key=v2 형태로 변환
// 하지만 빗썸 API는 key[]=v1&key[]=v2 형태가 필요
const query = params.order_ids
.map(id => `order_ids[]=${id}`)
.join('&');
const hash = crypto.createHash('SHA512');
const queryHash = hash.update(query, 'utf-8').digest('hex');주의사항
| 항목 | 설명 |
|---|---|
| 토큰 재사용 금지 | 요청마다 새로운 nonce와 timestamp로 토큰을 생성하세요. 동일 토큰을 재사용하면 인증이 거부됩니다. |
| Secret Key 보안 | Secret Key는 발급 시 한 번만 표시됩니다. 소스 코드에 직접 작성하지 말고 환경 변수나 .env 파일로 관리하세요. (개발 환경 설정하기 참고) |
| Content-Type | POST, DELETE 요청 시 body가 있으면 Content-Type: application/json; charset=utf-8 헤더를 포함하세요. |
| query_hash 누락 | 파라미터가 있는데 query_hash를 빠뜨리면 401 Unauthorized 에러가 발생합니다. |
| 배열 파라미터 | key[]=value 형태로 전개해야 합니다. 쉼표 구분(key=v1,v2)은 올바른 해시를 생성하지 않습니다. |
자주 발생하는 에러와 해결 방법
인증 관련 오류는 대부분 JWT 서명이 올바르게 되지 않았을 때 발생합니다.
| HTTP 상태 코드 | 에러 코드 | 원인 | 해결 방법 |
|---|---|---|---|
| 401 | invalid_query_payload | JWT query 검증 실패 | query_hash가 요청 파라미터와 일치하는지 확인하세요. |
| 401 | jwt_verification | JWT 토큰 검증 실패 | Secret Key와 서명 알고리즘(HS256)을 확인하세요. |
| 401 | expired_jwt | JWT 만료 | 요청마다 새로운 nonce와 timestamp를 생성하세요. |
| 401 | NotAllowIP | 허용되지 않은 IP 접근 | 빗썸 사이트에서 등록한 IP 주소를 확인하세요. |
| 401 | out_of_scope | API Key 권한 부족 | 빗썸 사이트에서 API 활성화 항목을 확인하세요. |
전체 에러 코드는 API 주요 에러 코드 문서를 참고하세요.
다음 단계
Updated about 10 hours ago
