AlgoLab Blog · 업비트 Open API · 포켓(Pocket) · 2026

업비트 포켓 API 7종 — 자산 이전 경로가 2개다

업비트 · 포켓/자산이전 2026-09-18 · 약 8분 읽기 · 알고랩 AlgoLab
한 줄 요약 업비트 포켓(Pocket) API는 7종이고, 자산 이전만 엔드포인트가 둘로 갈립니다 — 메인포켓 키로 지시하면 POST /v1/pockets/universal_transfers, 서브포켓 키로 내보내면 POST /v1/pockets/transfers입니다. 차이는 from 필드 하나입니다. universal_transfers에는 from이 있어 서브포켓끼리도 옮길 수 있고, transfers에는 아예 없어 언제나 자기 포켓에서만 나갑니다. 그리고 이전은 비동기statesubmitted로 시작합니다 — POST가 200을 돌려줘도 자산은 아직 도착하지 않았습니다.

업비트는 2026년 6월 1일 포켓(Pocket) 기능과 전용 API를 내놓았고, 6월 25일에는 거래·자산 관리 API의 Rate Limit 측정 단위를 계정에서 포켓으로 바꿨습니다. 이 변화는 "새 기능이 하나 늘었다"보다 큽니다 — 이미 돌고 있는 봇의 잔고 조회 결과와 요청 한도 계산이 같이 움직였기 때문입니다. 아래는 업비트 개발자 센터의 포켓 API Reference 원문과 변경 공지를 2026년 9월 18일 기준으로 대조해, 봇을 고쳐야 하는 지점만 옮긴 것입니다.

이 글에서 다루는 것

  1. 포켓 구조 — 메인포켓 1개 + 서브포켓 최대 5개
  2. 포켓 API 7종 전체 경로표
  3. 자산 이전이 두 갈래인 이유 — from 하나 차이
  4. 이전은 비동기다 — submitted에서 시작하는 상태 4단계
  5. GET /v1/accounts의 의미가 바뀌었다
  6. 권한 9종과 out_of_scope
  7. Rate Limit이 포켓 단위가 됐다 — 키를 늘려도 소용없다

1. 포켓 구조 — 메인 1개 + 서브 최대 5개

포켓은 업비트 계정 안에서 자산을 목적별로 나눠 두는 칸입니다. 계정을 만들면 메인포켓이 1개 주어지고, 여기에 서브포켓을 최대 5개까지 추가할 수 있습니다. 공식 명세의 type 값은 두 가지뿐입니다.

구분type 값개수외부 입출금계정 전체 관리
메인포켓main1개(고정)가능가능(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가 어느 포켓 소속이냐로 결정됩니다.

메인포켓 KEY 서브포켓 KEY POST /v1/pockets/ universal_transfers POST /v1/pockets/ transfers from (생략 시 = 내 포켓) from 없음 — 항상 내 포켓 to · currency · amount (필수) to · currency · amount (필수) 서브 → 서브 이전 가능 내보내기 전용 state: submitted → processing → done / failed
같은 body 3개(to·currency·amount), 갈리는 건 from 하나

두 요청의 필수 필드는 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 상태코드만 보고 다음 줄에서 잔고를 읽는 코드는 이전 전 잔고를 보고 주문 수량을 계산합니다. 에러가 나지 않기 때문에 로그에도 안 남고, "가끔 주문 수량이 이상하다"로만 나타납니다. statedone이 될 때까지 폴링한 뒤 주문 단계로 넘어가야 합니다.

확인은 목록 조회로 합니다. GET /v1/pockets/transfers에는 uuids[]·identifiers[](각 최대 20개), currency, start_time·end_time, limit(기본 20, 최대 100), 그리고 방향을 거르는 direction(in·out·all)이 있습니다.

조회 기간은 최대 7일입니다. start_time·end_timeISO 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/pocketsout_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-all2초당 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회"를 세고 있었다면 기준을 포켓 단위로 쪼개야 합니다. 참고로 minDeprecated라 실제 계산에 쓰이지 않습니다 — 분 단위 제한은 2026년 8월 21일 상향 공지와 함께 더 이상 적용되지 않습니다. (같은 공지로 order 그룹이 초당 8회 → 12회가 됐습니다.)

초과 시 동작도 포켓 기준으로 바뀌었습니다. 429 Too Many Requests는 초당 한도 초과, 418 I'm a teapot은 429가 누적된 일시 차단이고 차단 단위가 IP·포켓·커넥션입니다. 즉 한 서브포켓이 418을 맞아도 다른 포켓은 멀쩡합니다. 정책 위반이 반복되면 차단 시간이 점진적으로 늘어나므로 418에 무작정 재시도를 거는 로직은 상황을 악화시킵니다 (호출 한도 설계 쪽에 백오프 패턴을 정리해 뒀습니다).

8. 정리 — 오늘 봇에서 확인할 다섯 줄

  1. GET /v1/accounts를 총자산으로 쓰고 있는가 → 포켓 잔고다. 서브포켓을 만들 계획이면 합산 로직이 필요하다.
  2. 이전 후 곧바로 잔고를 읽는가 → state == "done" 확인을 사이에 넣는다.
  3. 이전 요청에 identifier가 있는가 → 없으면 재시도가 곧 이중 이전이다.
  4. 한도를 자체 카운터로 세는가 → 계정이 아니라 포켓 기준으로 쪼갠다.
  5. 처리량을 늘리려고 키를 더 발급했는가 → 같은 포켓이면 효과 0. 포켓을 나눈다.

포켓은 "자산을 나눠 두는 편의 기능"처럼 보이지만, 자동매매 쪽에서는 격리 단위가 하나 생긴 것에 가깝습니다 — 잔고도, 권한도, 요청 한도도, 일시 차단도 포켓 경계에서 끊깁니다.

자주 묻는 것

업비트 포켓(Pocket)이 무엇인가요?

계정 안에서 자산을 목적별로 나누는 단위입니다. 메인포켓 1개에 서브포켓 최대 5개, 명세상 typemainuser_spot_trading 둘뿐입니다. 서브포켓은 외부 입출금이 제한되고 manage_pockets는 메인포켓 전용입니다.

포켓 간 자산 이전 API는 어느 것을 써야 하나요?

키가 속한 포켓에 따라 갈립니다. 메인포켓 키는 POST /v1/pockets/universal_transfers, 서브포켓 키는 POST /v1/pockets/transfers입니다. 필수 필드는 둘 다 to·currency·amount이고, 차이는 from입니다 — universal_transfers에만 있고 생략하면 그 키의 포켓이 기본값입니다.

자산을 이전하면 바로 주문에 쓸 수 있나요?

아닙니다. 비동기submittedprocessingdone/failed로 갑니다. POST가 200이어도 statesubmitted이므로, 목록 조회로 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시간 빠른 답변 가능합니다.

무료로 상담하기
본 글은 업비트 개발자 센터(docs.upbit.com)의 포켓 관련 API Reference 원문과 OpenAPI 정의, 포켓 기능 출시 공지(2026-06-01 적용)·Rate Limit 적용 단위 변경 공지(2026-06-25 적용)·Rate Limit 상향 공지(2026-08-21 적용)를 2026년 9월 18일 기준으로 대조해 작성했습니다. 본문의 경로·파라미터·권한·한도 수치는 전부 그 시점의 공식 문서 표기이며, 실계좌 호출로 검증한 것이 아닙니다. 엔드포인트·필드·한도 정책은 공지 후 변경될 수 있으므로 반드시 업비트 개발자 센터의 현재 문서로 대조하십시오. 특히 서브포켓 잔고 경로는 문서 내 표기가 갈리는 지점이라 본문에 그 사실을 함께 적었습니다. 이 글은 특정 종목이나 매매 시점에 대한 권유를 담고 있지 않으며, 수익률이나 시장 방향에 대한 어떠한 전망도 하지 않습니다. 알고랩(퀀트웍스)은 투자자문업·투자일임업을 영위하지 않으며, 고객이 정한 규칙을 코드로 구현하는 도구 제작 서비스를 제공합니다. 투자 판단과 그 결과의 책임은 전적으로 투자자 본인에게 있습니다.