업비트(Upbit)는 한국을 대표하는 중앙화 거래소(CEX) 중 하나로, 퍼블릭 API와 프라이빗 API를 제공한다. 퍼블릭 API는 시장 데이터를 누구나 조회할 수 있으며, 프라이빗 API는 개인 계정 정보와 주문 기능을 다룰 수 있다. 본 글에서는 퍼블릭 API 중심으로 Python을 이용해 데이터를 조회하고 분석하는 방법을 정리하였다.
- 참고 자료
- 업비트 개발자 센터(글로벌/KR)와 엔드포인트·레이트리밋 안내
- https://global-docs.upbit.com
- 업비트 개발자 센터(글로벌/KR)와 엔드포인트·레이트리밋 안내
환경 세팅 (선택)
분석 환경은 Python 3.11 환경을 기준으로 한다. 새로운 가상환경을 만드는 것이 권장되지만, 기존에 사용하는 파이썬 환경에 바로 패키지를 설치해도 무방하다
- PowerShell, VS Code/Cursor 터미널, Mac/Linux 터미널에서 모두 실행 가능하다.
conda create -n binance311 python=3.11 -y conda activate binance311 pip install -U pip
환경을 활성화한 후 필요한 기본 패키지를 설치한다.
pip install requests websockets pandas python-dotenv
- requests : REST API 요청을 보내고 응답을 받는 데 사용한다.
- websockets : 실시간 시세와 체결 데이터를 스트리밍으로 수집할 때 사용한다.
- pandas : 수집한 데이터를 데이터프레임 형태로 정리하고 분석할 때 사용한다.
- python-dotenv : API 키와 같은 민감한 정보를
.env파일로 관리할 때 사용한다
이 환경을 기반으로 바이낸스 API에 접근하여 시장 데이터를 수집하고, 데이터프레임으로 가공하여 분석하는 과정을 단계별로 진행한다.
업비트(Upbit) API 특징
- 프라이빗은 JWT(HS256) 서명 토큰을 생성해
Authorization헤더로 보낸다. (요청 바디는 JSON 권장) Upbit Global Docs - 레이트 리밋: 초당 요청 수를 제한하며, 응답 헤더
Remaining-Req로 잔여량을 확인한다(초 단위 기준). 예:group=default; sec=29이면 현재 초에 29회 가능. 최근 정책은 계정 단위로 측정
| 구분 | 주요 특징 | 제공 데이터 | 인증 |
|---|---|---|---|
| 퍼블릭 API | 시장 데이터, 누구나 접근 | 현재가/호가/체결/캔들/마켓목록 | 불필요 |
| 프라이빗 API | 계정·주문·잔고 등 | 잔고/주문/체결이력 등 | JWT 토큰(Access/Secret) 필요 |
퍼블릭 API 데이터 종류 (요약 표)
업비트 글로벌 문서 기준 엔드포인트 예시(국가별 베이스 URL 상이). 한국 KR 기준 /v1/... 패스, 글로벌은 https://sg-api.upbit.com 등 리전에 따라 Base URL이 다르다.
| 구분 | 엔드포인트 | 설명 | 주요 필드(내용) |
|---|---|---|---|
| 마켓 목록 | /v1/market/all | 상장 심볼 목록 반환 | market(마켓 ID), korean_name(한글명), english_name(영문명) |
| 현재가 | /v1/ticker | 지정 마켓들의 현재가 및 변동 | market(마켓 ID), trade_price(체결 가격), acc_trade_volume_24h(24시간 누적 거래량) |
| 호가창 | /v1/orderbook | 1~30 레벨 호가 스냅샷 | orderbook_units[]{bid_price(매수호가), ask_price(매도호가), bid_size(매수 잔량), ask_size(매도 잔량), total_bid_size(총 매수 잔량), total_ask_size`(총 매도 잔량) |
| 최근 체결 | /v1/trades/ticks | 최신 체결 틱 데이터 | trade_price(체결 가격), trade_volume(체결 수량), ask_bid(매수/매도 구분), timestamp(체결 시각, ms) |
| 캔들(분) | /v1/candles/minutes/{unit} | 분 단위 OHLCV 데이터 | opening_price(시가), high_price(고가), low_price(저가), trade_price(종가), candle_acc_trade_volume(누적 거래량), timestamp(시각, ms) |
| 캔들(일/주/월) | /v1/candles/days, /weeks, /months | 일/주/월 단위 OHLCV 데이터 | opening_price(시가), high_price(고가), low_price(저가), trade_price(종가), candle_acc_trade_volume(누적 거래량), timestamp(시각, ms) |
퍼블릭 API 사용 시 주의사항
- 호출 횟수 제한 (Rate Limit)
- 업비트 API는 초당 요청 수 제한이 있다.
- 응답 헤더에 포함된
Remaining-Req값을 통해 현재 남은 호출 가능 횟수를 확인할 수 있다.- 예:
group=default; min=599; sec=29→ 현재 초(sec)에 29회, 현재 분(min)에 599회 남음.
- 예:
- 한도를 초과하면 HTTP 429 Too Many Requests 오류가 발생한다.
- 데이터 조회 제한
- 호가창(orderbook): 최대 30호가 레벨까지만 제공된다.
- 최근 체결(trades/ticks): 최대 200건까지 한 번에 조회 가능.
- 캔들(candles):
count파라미터로 지정 가능, 기본 1~200개 범위 지원. - 마켓 목록/현재가: 요청 마켓 수가 많을수록 응답이 커지므로, 불필요하게 모든 마켓을 반복 요청하지 않도록 주의.
- 데이터 시차
- 캔들 데이터는 거래가 없는 시간대에는 생성되지 않을 수 있다 (빈 캔들 발생).
- 따라서 시계열 데이터 분석 시 누락된 시간대 보정이 필요할 수 있다.
업비트 퍼블릭 API 데이터 조회하기
라이브러리
import requests import pandas as pd
- requests : API 요청을 보내고 응답(JSON)을 받아오는 데 사용한다.
- pandas : JSON 데이터를 표 형태(DataFrame)로 변환하여 분석하기 쉽게 만들어준다.
요청-응답 기본 구조
import requests
import pandas as pd
BASE = "https://api.upbit.com"
def get_json(path, params=None, timeout=10):
"""업비트 퍼블릭 API용 GET 호출 헬퍼 함수이다."""
url = f"{BASE}{path}" # 호출할 전체 URL을 만든다.
res = requests.get(url, params=params, timeout=timeout) # GET 요청을 보낸다.
res.raise_for_status() # HTTP 오류(4xx/5xx) 발생 시 예외를 던진다.
# print(res.headers.get("Remaining-Req")) # (선택) 남은 호출 가능 횟수 확인용이다.
return res.json() # JSON 응답을 파이썬 객체로 변환해 반환한다.
- url : 엔드포인트 절대 경로 (예:
https://api.upbit.com/v1/ticker) - params : 쿼리 파라미터 (예:
{"markets": "KRW-BTC"}) - requests.get(…) : GET 요청 전송 (
timeout지정 권장) - res.raise_for_status() : HTTP 오류 감지
- res.json() : JSON → Python 객체 변환
- print(…) : 데이터 확인
데이터 호출하기
- 현재가 조회하기
# 업비트 현재가 API: /v1/ticker
# markets 파라미터는 콤마로 여러 마켓을 지정할 수 있다 (예: KRW-BTC,KRW-ETH).
data = get_json("/v1/ticker", params={"markets": "KRW-BTC,KRW-ETH"})
print(data) # 리스트로 반환된다.
# pandas로 일부 필드만 확인한다.
df = pd.DataFrame(data)
print(df[["market", "trade_price", "acc_trade_volume_24h"]].head())
- 호가창 조회
# 업비트 호가창 API: /v1/orderbook
# 상위 1~30 레벨까지의 호가 스냅샷을 제공한다.
ob = get_json("/v1/orderbook", params={"markets": "KRW-BTC"})
print(ob[0].keys()) # orderbook_units, total_ask_size, total_bid_size 등 키를 확인한다.
# 호가 레벨 테이블로 변환한다.
units = pd.DataFrame(ob[0]["orderbook_units"]) # bid_price, ask_price, bid_size, ask_size
print(units.head()) # 상위 5개 레벨만 확인한다.
- 최근 체결 조회
# 업비트 최근 체결 틱: /v1/trades/ticks
# count 최대 200까지 가능하며 최신 체결이 먼저 온다(시간 역순) .
trades = get_json("/v1/trades/ticks", params={"market": "KRW-BTC", "count": 5})
print(trades) # 리스트로 반환된다.
# DataFrame으로 변환해 핵심 필드만 확인한다.
tdf = pd.DataFrame(trades)
print(tdf[["trade_price", "trade_volume", "ask_bid", "timestamp"]])
- 캔들 조회 (분 단위 예: 60분봉 5개)
# 업비트 캔들(분): /v1/candles/minutes/{unit}
# unit은 1, 3, 5, 10, 30, 60 중 하나이다.
candles = get_json("/v1/candles/minutes/60", params={"market": "KRW-BTC", "count": 5})
df = pd.DataFrame(candles)
# 시간 컬럼을 가독성 있게 변환/정리한다.
df["candle_date_time_kst"] = pd.to_datetime(df["candle_date_time_kst"])
print(df[["candle_date_time_kst", "opening_price", "high_price", "low_price", "trade_price",
"candle_acc_trade_volume"]])
데이터 호출 시 주의사항
- 호출 횟수 제한
- 초당 요청 수 제한이 있으며, 응답 헤더
Remaining-Req에서 남은 호출 횟수를 확인할 수 있다. - 제한을 초과하면 HTTP 429 오류가 발생한다.
- 초당 요청 수 제한이 있으며, 응답 헤더
- 데이터 조회 제한
- 호가창: 최대 30레벨
- 체결: 최대 200건
- 캔들: 최대 200개
- 데이터 특성
- 체결 데이터는 최신순(역순)으로 반환 → 분석 시 시간 정렬 필요
- 캔들은 거래 없는 구간이 비어 있을 수 있음 → 시계열 분석 시 보정 필요
- 에러 처리
res.raise_for_status()로 HTTP 오류 감지- 429 오류 발생 시 딜레이 후 재시도(backoff 전략 권장)
timeout지정으로 네트워크 지연 방지