업비트 포켓 API 7종 — 자산 이전 경로가 2개다
POST /v1/pockets/universal_transfers,
서브포켓 키로 내보내면 POST /v1/pockets/transfers입니다.
차이는 from 필드 하나입니다. universal_transfers에는 from이 있어
서브포켓끼리도 옮길 수 있고, transfers에는 아예 없어 언제나 자기 포켓에서만 나갑니다.
그리고 이전은 비동기라 state가 submitted로 시작합니다 —
POST가 200을 돌려줘도 자산은 아직 도착하지 않았습니다.
업비트는 2026년 6월 1일 포켓(Pocket) 기능과 전용 API를 내놓았고, 6월 25일에는 거래·자산 관리 API의 Rate Limit 측정 단위를 계정에서 포켓으로 바꿨습니다. 이 변화는 "새 기능이 하나 늘었다"보다 큽니다 — 이미 돌고 있는 봇의 잔고 조회 결과와 요청 한도 계산이 같이 움직였기 때문입니다. 아래는 업비트 개발자 센터의 포켓 API Reference 원문과 변경 공지를 2026년 9월 18일 기준으로 대조해, 봇을 고쳐야 하는 지점만 옮긴 것입니다.
이 글에서 다루는 것
- 포켓 구조 — 메인포켓 1개 + 서브포켓 최대 5개
- 포켓 API 7종 전체 경로표
- 자산 이전이 두 갈래인 이유 —
from하나 차이 - 이전은 비동기다 —
submitted에서 시작하는 상태 4단계 GET /v1/accounts의 의미가 바뀌었다- 권한 9종과
out_of_scope - Rate Limit이 포켓 단위가 됐다 — 키를 늘려도 소용없다
1. 포켓 구조 — 메인 1개 + 서브 최대 5개
포켓은 업비트 계정 안에서 자산을 목적별로 나눠 두는 칸입니다.
계정을 만들면 메인포켓이 1개 주어지고, 여기에 서브포켓을 최대 5개까지 추가할 수 있습니다.
공식 명세의 type 값은 두 가지뿐입니다.
| 구분 | type 값 | 개수 | 외부 입출금 | 계정 전체 관리 |
|---|---|---|---|---|
| 메인포켓 | main | 1개(고정) | 가능 | 가능(manage_pockets) |
| 서브포켓 | user_spot_trading | 최대 5개 | 제한 | 불가 |
자동매매 관점에서 의미 있는 것은 서브포켓마다 API Key를 따로 발급하고 권한을 다르게 줄 수 있다는 점입니다. 전략 A의 키로는 전략 B의 잔고가 보이지 않고, 전략 A가 한도를 다 써도 전략 B는 멀쩡합니다 — 여러 전략을 한 계정에서 돌릴 때의 "한 봇이 잔고를 다 긁어가는" 구조를 거래소가 막아 주는 셈입니다.
서브포켓은 외부 입출금이 제한됩니다. 원화 입금이나 코인 출금은 메인포켓 쪽이고, 서브포켓은 계정 안에서만 움직입니다. 즉 서브포켓 키 하나가 통째로 새도 외부로 빼낼 수 있는 경로가 구조적으로 없습니다 — API 키 보안 관점에서 이게 포켓의 가장 큰 실익입니다.
2. 포켓 API 7종 — 경로표
업비트가 신규로 추가했다고 공지한 포켓 API는 7종입니다.
그런데 경로(path) 기준으로는 5개고, 그중 두 개가 POST·GET을 나눠 쓰기 때문에 동작이 7개가 됩니다.
| 기능 | 메서드 · 경로 | 사용 포켓 | 필요 권한 |
|---|---|---|---|
| 포켓 정보 조회 | GET /v1/pockets | 메인포켓 | manage_pockets |
| 포켓별 API Key 목록 | GET /v1/pockets/api_keys | 메인포켓 | manage_pockets |
| 서브포켓 잔고 조회 | GET /v1/pockets/assets | 메인포켓 | manage_pockets |
| 메인포켓 자산 이전 | POST /v1/pockets/universal_transfers | 메인포켓 | transfer |
| 메인포켓 이전 내역 | GET /v1/pockets/universal_transfers | 메인포켓 | transfer |
| 서브포켓 자산 이전 | POST /v1/pockets/transfers | 서브포켓 | transfer |
| 서브포켓 이전 내역 | GET /v1/pockets/transfers | 서브포켓 | transfer |
⚠ 서브포켓 잔고 경로는 복수형 assets입니다.
포켓 정보 조회 문서에 실린 흐름도에는 GET /v1/pockets/asset(단수)으로 그려져 있지만,
같은 사이트의 서브포켓 잔고 조회 페이지에 실린 OpenAPI 정의는 /v1/pockets/assets입니다.
문서 안에서 표기가 갈리는 지점이라 단수로 짜면 404를 봅니다.
포켓 API는 전부 UUID로 상대를 지목하므로 봇의 첫 호출은 거의 항상 GET /v1/pockets입니다.
GET /v1/pockets
[
{
"uuid": "9ca023a5-851b-4fec-9f0a-48cd83c2eaae",
"name": "단기 트레이딩 계좌",
"type": "user_spot_trading"
}
]
돌아오는 필드는 uuid·name·type 셋이고 전부 필수 항목입니다.
name은 사용자가 업비트 화면에서 붙인 이름이라 언제든 바뀔 수 있습니다 —
코드에서 포켓을 고를 때 이름으로 매칭하면 사용자가 이름만 바꿔도 봇이 엉뚱한 포켓에 주문을 냅니다.
UUID를 설정 파일에 박아 두고 이름은 로그 표시용으로만 쓰는 편이 안전합니다.
3. 자산 이전이 두 갈래인 이유 — from 하나 차이
포켓 간 자산 이전은 같은 일을 하는 엔드포인트가 두 개이고, 어느 쪽을 쓰는지는 내가 든 API Key가 어느 포켓 소속이냐로 결정됩니다.
두 요청의 필수 필드는 to·currency·amount로 동일합니다.
차이는 from입니다.
// 메인포켓 키 — 서브포켓 A에서 서브포켓 B로 직접 옮길 수 있다
POST /v1/pockets/universal_transfers
{
"from": "9ca023a5-851b-4fec-9f0a-48cd83c2eaae",
"to": "1f20b8c4-77aa-4a10-9d31-0c6f3d2e5b19",
"currency": "KRW",
"amount": "500000",
"identifier": "rebal_20260918_01"
}
// 서브포켓 키 — from이 없다. 언제나 내 포켓에서 나간다
POST /v1/pockets/transfers
{
"to": "1f20b8c4-77aa-4a10-9d31-0c6f3d2e5b19",
"currency": "KRW",
"amount": "500000",
"identifier": "rebal_20260918_01"
}
universal_transfers에서 from을 생략하면 요청에 쓰인 키가 속한 포켓이 기본값이 됩니다.
즉 메인포켓 키로 from 없이 보내면 메인포켓에서 나갑니다.
공식 문서가 명시하는 메인포켓 키의 이전 가능 범위는 메인→서브, 서브→메인, 서브→서브 세 방향입니다.
identifier는 중복 요청을 막는 열쇠입니다.
클라이언트가 지정하는 식별자이고 1~64자의 영문·숫자·언더스코어(_)·점(.)·하이픈(-)만 허용합니다.
지정하지 않으면 응답에 null로 내려옵니다. 리밸런싱처럼 재시도가 곧 이중 이전이 되는 작업에서는
날짜와 회차를 조합한 값을 반드시 넣으십시오. 조회할 때도 identifiers[]로 되짚을 수 있습니다.
4. 이전은 비동기다 — submitted에서 시작한다
가장 조용히 사고가 나는 지점입니다. 포켓 자산 이전은 즉시 반영이 아닙니다.
응답의 state 값은 네 가지입니다.
| state | 의미 | 봇이 해야 할 일 |
|---|---|---|
submitted | 이전 요청이 접수됨 | 대기 — 아직 잔고에 없다 |
processing | 처리 중 | 대기 |
done | 이전 완료 | 이제 주문 단계로 |
failed | 이전 실패 | 분기 필수 — 재시도는 같은 identifier로 |
POST /v1/pockets/transfers → 200 OK
{
"uuid": "9ca023a5-851b-4fec-9f0a-48cd83c2eaae",
"identifier": "rebal_20260918_01",
"from": "9ca023a5-851b-4fec-9f0a-48cd83c2eaae",
"to": "1f20b8c4-77aa-4a10-9d31-0c6f3d2e5b19",
"state": "submitted",
"currency": "KRW",
"amount": "500000",
"created_at": "2026-05-28T15:17:51+09:00"
}
⚠ 200 OK와 "state": "submitted"가 같이 옵니다.
HTTP 상태코드만 보고 다음 줄에서 잔고를 읽는 코드는 이전 전 잔고를 보고 주문 수량을 계산합니다.
에러가 나지 않기 때문에 로그에도 안 남고, "가끔 주문 수량이 이상하다"로만 나타납니다.
state가 done이 될 때까지 폴링한 뒤 주문 단계로 넘어가야 합니다.
확인은 목록 조회로 합니다. GET /v1/pockets/transfers에는
uuids[]·identifiers[](각 최대 20개), currency,
start_time·end_time, limit(기본 20, 최대 100),
그리고 방향을 거르는 direction(in·out·all)이 있습니다.
조회 기간은 최대 7일입니다. start_time·end_time은
ISO 8601 또는 밀리초 타임스탬프를 받고, end_time의 기본값은 현재 시각입니다.
한 달치 이전 내역을 한 번에 긁는 리포트를 짜려면 7일씩 끊어 도는 루프가 필요합니다.
5. GET /v1/accounts의 의미가 바뀌었다
기존에 돌던 업비트 봇이 거의 예외 없이 쓰는 호출입니다.
공식 API Reference에서 GET /v1/accounts는 이제 "포켓 잔고 조회"로 분류되어 있고,
요청에 쓰인 키가 속한 포켓의 잔고를 돌려줍니다.
응답 스키마는 예전 그대로입니다 — currency, balance, locked,
avg_buy_price, avg_buy_price_modified, unit_currency.
바뀐 것은 필드가 아니라 집계 범위입니다.
GET /v1/accounts // 이 키가 속한 "포켓"의 잔고
[
{
"currency": "BTC",
"balance": "2.0",
"locked": "0.0",
"avg_buy_price": "140000000",
"avg_buy_price_modified": false,
"unit_currency": "KRW"
}
]
⚠ 서브포켓에 자산을 나눠 둔 뒤 메인포켓 키로 /v1/accounts를 부르면
서브포켓 자산은 응답에 없습니다.
에러도 경고도 없이 총자산이 줄어든 것처럼 보일 뿐이라,
"잔고의 몇 %를 산다" 같은 포지션 사이징 로직은
조용히 작은 수량을 냅니다. 계정 전체를 보려면 GET /v1/pockets로 목록을 받아
서브포켓별 GET /v1/pockets/assets?uuid=...를 합산해야 합니다.
6. 권한 9종과 out_of_scope
포켓별 API Key 목록 조회(GET /v1/pockets/api_keys)의 응답에는
access_key·permissions·allowed_ips·created_at·expired_at가 들어 있고,
permissions에 올 수 있는 값은 명세상 9종입니다.
| 권한 | 하는 일 |
|---|---|
view_account | 잔고 조회 |
view_orders / make_orders | 주문 조회 / 주문 생성·취소 |
view_deposits / deposit | 입금 조회 / 입금 주소 생성 |
view_withdrawals / withdraw | 출금 조회 / 출금 실행 |
transfer | 포켓 간 자산 이전 |
manage_pockets | 포켓 조회·전체 관리 (메인포켓 전용) |
권한이 없는 키로 호출하면 out_of_scope 오류가 납니다.
GET /v1/pockets가 out_of_scope로 떨어졌다면 인증이 틀린 것이 아니라
그 키에 manage_pockets가 안 붙어 있는 것입니다.
JWT 서명이나 query_hash를 뜯어보기 전에 여기부터 보는 편이 빠릅니다
(업비트 REST·WebSocket 정리에 인증 흐름이 있습니다).
키 발급 한도와 수명도 포켓 단위입니다.
공지 기준 메인포켓 최대 10개, 서브포켓 최대 5개이며,
API Key는 1년간 유효하고 연장할 수 없습니다 — 만료되면 삭제 후 재발급입니다.
응답의 expired_at을 읽어 만료 임박을 미리 알려 두면 어느 날 아침 봇이 통째로 멈추는 일을 피할 수 있습니다.
allowed_ips가 있다는 데서 보이듯 키는 등록된 IP에서만 동작합니다.
7. Rate Limit이 포켓 단위가 됐다 — 키를 늘려도 소용없다
2026년 6월 25일부로 거래·자산 관리(Exchange) REST API와 인증 포함 WebSocket 연결의 Rate Limit 측정 단위가 계정에서 포켓으로 바뀌었습니다. 그룹별 초당 한도 숫자는 그대로이고, 차감 기준만 달라졌습니다.
| 그룹 | 한도 | 측정 단위 | 대상 |
|---|---|---|---|
exchange.default | 초당 30회 | 포켓 | 포켓 API 7종·잔고·주문 조회·개별 취소·입출금 |
exchange.order | 초당 12회 | 포켓 | 주문 생성 · 취소 후 재주문 |
exchange.order-test | 초당 8회 | 포켓 | 주문 생성 테스트 |
exchange.order-cancel-all | 2초당 1회 | 포켓 | 주문 일괄 취소 |
시세(market·candle·ticker 등) | 각 초당 10회 | IP | 변경 없음 |
여기서 실무적으로 가장 중요한 문장은 공식 문서의 이 한 줄입니다 —
같은 포켓의 여러 API Key는 하나의 한도를 공유한다.
처리량을 늘리려고 키를 더 발급하는 것은 아무 효과가 없습니다.
늘려야 하는 것은 포켓입니다. 문서가 드는 예시는
메인포켓 1개 + 서브포켓 5개가 각각 exchange.default 초당 30회를 독립 보유해
계정 합산 최대 초당 180회가 되는 경우입니다(개별 포켓은 그대로 30회).
헤더로 관리하고 있었다면 코드 변경이 없습니다.
응답의 Remaining-Req: group=default; min=1800; sec=29에서
sec 값을 읽어 감속하는 방식이라면 그대로 동작합니다.
반대로 자체 카운터로 "계정 기준 초당 N회"를 세고 있었다면 기준을 포켓 단위로 쪼개야 합니다.
참고로 min은 Deprecated라 실제 계산에 쓰이지 않습니다 —
분 단위 제한은 2026년 8월 21일 상향 공지와 함께 더 이상 적용되지 않습니다.
(같은 공지로 order 그룹이 초당 8회 → 12회가 됐습니다.)
초과 시 동작도 포켓 기준으로 바뀌었습니다. 429 Too Many Requests는 초당 한도 초과,
418 I'm a teapot은 429가 누적된 일시 차단이고
차단 단위가 IP·포켓·커넥션입니다. 즉 한 서브포켓이 418을 맞아도 다른 포켓은 멀쩡합니다.
정책 위반이 반복되면 차단 시간이 점진적으로 늘어나므로 418에 무작정 재시도를 거는 로직은 상황을 악화시킵니다
(호출 한도 설계 쪽에 백오프 패턴을 정리해 뒀습니다).
8. 정리 — 오늘 봇에서 확인할 다섯 줄
GET /v1/accounts를 총자산으로 쓰고 있는가 → 포켓 잔고다. 서브포켓을 만들 계획이면 합산 로직이 필요하다.- 이전 후 곧바로 잔고를 읽는가 →
state == "done"확인을 사이에 넣는다. - 이전 요청에
identifier가 있는가 → 없으면 재시도가 곧 이중 이전이다. - 한도를 자체 카운터로 세는가 → 계정이 아니라 포켓 기준으로 쪼갠다.
- 처리량을 늘리려고 키를 더 발급했는가 → 같은 포켓이면 효과 0. 포켓을 나눈다.
포켓은 "자산을 나눠 두는 편의 기능"처럼 보이지만, 자동매매 쪽에서는 격리 단위가 하나 생긴 것에 가깝습니다 — 잔고도, 권한도, 요청 한도도, 일시 차단도 포켓 경계에서 끊깁니다.
자주 묻는 것
업비트 포켓(Pocket)이 무엇인가요?
계정 안에서 자산을 목적별로 나누는 단위입니다. 메인포켓 1개에 서브포켓 최대 5개,
명세상 type은 main과 user_spot_trading 둘뿐입니다.
서브포켓은 외부 입출금이 제한되고 manage_pockets는 메인포켓 전용입니다.
포켓 간 자산 이전 API는 어느 것을 써야 하나요?
키가 속한 포켓에 따라 갈립니다. 메인포켓 키는 POST /v1/pockets/universal_transfers,
서브포켓 키는 POST /v1/pockets/transfers입니다.
필수 필드는 둘 다 to·currency·amount이고,
차이는 from입니다 — universal_transfers에만 있고 생략하면 그 키의 포켓이 기본값입니다.
자산을 이전하면 바로 주문에 쓸 수 있나요?
아닙니다. 비동기라 submitted → processing →
done/failed로 갑니다. POST가 200이어도 state는 submitted이므로,
목록 조회로 done을 확인한 뒤 주문으로 넘어가되 failed 분기를 두십시오.
API Key를 늘리면 요청 한도가 늘어나나요?
아닙니다. 2026-06-25부터 Exchange API 한도는 포켓 단위이고 같은 포켓의 여러 키는 한도를 공유합니다. 공식 예시는 메인 1 + 서브 5 = 계정 합산 초당 180회 (개별 포켓은 30회 그대로). 시세 조회는 여전히 IP 단위입니다. 한도 정책은 공지 후 바뀌므로 개발자 센터의 현재 문서로 확인하십시오.
기존 봇의 GET /v1/accounts는 그대로 써도 되나요?
호출은 되지만 의미가 달라졌습니다 — 이제 그 키가 속한 포켓의 잔고입니다.
응답 스키마는 그대로라 에러 없이 조용히 적은 잔고를 보고 움직이는 형태로 나타납니다.
계정 전체가 필요하면 GET /v1/pockets + 서브포켓별 GET /v1/pockets/assets를 합산하십시오.
전략별로 자금을 나눠 돌리고 싶다면
포켓을 몇 개로 쪼갤지, 이전을 자동화할지 수동으로 둘지는 전략 수와 회전율에 따라 답이 다릅니다.
구조부터 같이 정리해 드립니다. 24시간 빠른 답변 가능합니다.