CCXT

CCXT vs the Coinone API

Coinone documents a REST and WebSocket API but ships no client library. CCXT versus the raw API on signing, streaming, coverage and portability.

Coinone is a Korean exchange trading mostly against KRW. Its developer centre at docs.coinone.co.kr documents a public REST API, a private REST API in two generations (v2.0 and v2.1), and a public WebSocket. What it does not publish is a client library in any language.

That makes the comparison here CCXT against your own HTTP client, and the question that decides it is whether you want to implement Coinone's signing, nonce and payload rules yourself.

TL;DR

  • Write against the raw API if you need a couple of endpoints, want no dependencies, and are comfortable with base64-encoded signed payloads and Coinone's IP allowlist.
  • Pick CCXT if you want Coinone behind an installable client that already handles signing, market metadata, throttling and order-book maintenance — and that speaks the same API on 103 other venues.
  • The unified layer is not a ceiling. All 63 Coinone endpoints CCXT knows about are generated as implicit methods, including the v2.0 endpoints the newer generation has not replaced.

At a glance

CCXTRaw Coinone API
Exchanges covered104 (Coinone is one of them)Coinone only
LanguagesTypeScript, JavaScript, Python, PHP, C#/.NET, Go, Java — one APIany language you write the signer in
Installable client libraryyes — ccxtnone published by Coinone
Unified market data + trading APIyes — 18 unified capabilities, 11 fetch* methodsCoinone's own request/response shapes
WebSocketsyes — 3 watch* methods (ticker, order book, trades)wss://public-ws-api.coinone.co.kr — 6 public channels plus private streams
Raw endpoint accessyes — 63 endpoints as implicit methodsit is the whole product
Built-in rate limiteryes, on by default (rateLimit 50ms)your code
Unified error typesyes — 41 typed exceptions in one hierarchyCoinone errorCode values
Testnet / sandboxnot available for Coinonenone documented
Popularity43.8k GitHub stars · 4.8M PyPI + 494k npm installs/monthn/a — no published package
LicenceMITn/a
SupportDiscord, Telegram, GitHub issues — usually same-dayCoinone developer centre

Figures verified September 2026 against CCXT v4.5.77 and the Coinone developer centre documentation at docs.coinone.co.kr.

The same job, written both ways

Fetch a ticker

import ccxt

exchange = ccxt.coinone()
ticker = exchange.fetch_ticker('BTC/KRW')
print(ticker['last'], ticker['baseVolume'])

Public market data is unsigned, so the raw call is short. CCXT returns a unified ticker structure, and — more usefully — 'BTC/KRW' is a portable symbol rather than a path segment pair you have to assemble as KRW/BTC in the venue's own order.

Place a limit order

import ccxt

exchange = ccxt.coinone({'apiKey': '...', 'secret': '...'})
order = exchange.create_order('BTC/KRW', 'limit', 'buy', 0.001, 90000000)
print(order['id'], order['status'])

Coinone's private API is a stack of easy-to-miss rules: the request body is the base64 string, the same string goes in X-COINONE-PAYLOAD, the signature is HMAC-SHA512 over that base64 rather than over the JSON, and the secret is uppercased before it is used as the key. Get any one of them wrong and you get an authentication failure with nothing to distinguish which. CCXT implements it once and tests it on every release.

One caveat worth knowing up front: CCXT's coinone class accepts limit orders only and raises ExchangeError for any other type.

Stream an order book

import ccxt.pro
import asyncio

async def main():
    exchange = ccxt.pro.coinone()
    while True:
        orderbook = await exchange.watch_order_book('BTC/KRW')
        print(orderbook['bids'][0], orderbook['asks'][0])

asyncio.run(main())

CCXT gives you a merged, depth-limited book that stays correct across reconnects. The raw socket gives you channel messages plus the responsibility for keeping the local book in step, answering the PING channel, and re-subscribing after a drop.

Where the differences actually bite

Two API generations, one client

Coinone's private API exists in two generations with different conventions: v2.1 uses snake_case throughout and a UUID v4 nonce, while v2.0 uses snake_case requests with camelCase responses and a millisecond-timestamp nonce that must always increase. CCXT's coinone class routes each unified method to the right generation and normalises both response shapes, so your code never sees the split. Both generations remain reachable as implicit methods if you need a v2.0-only endpoint.

Portability is the whole point

Korean venues have their own conventions — KRW quoting, quote_currency/target_currency path pairs, per-key IP allowlists of up to five IPv4 addresses. Adding Coinone to a portfolio that also touches global venues means reconciling all of that with everything else. In CCXT the venue is a variable:

for exchange_id in ['coinone', 'upbit', 'bithumb', 'binance']:
    exchange = getattr(ccxt, exchange_id)()
    print(exchange_id, exchange.fetch_ticker('BTC/KRW' if exchange_id != 'binance' else 'BTC/USDT')['last'])

One error hierarchy

CCXT maps Coinone's numeric error codes onto a typed exception treeInsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, RateLimitExceeded, NetworkError and 35 more, all descending from BaseError. You write except ccxt.InsufficientFunds once instead of matching on a code string that may change.

Precision and string math

KRW prices are large integers and BTC amounts are small decimals, which is exactly the combination that goes wrong under float arithmetic. CCXT loads Coinone's market metadata and gives you amount_to_precision, price_to_precision and cost_to_precision, backed by the Precise string-arithmetic class:

amount = exchange.amount_to_precision('BTC/KRW', 0.0012345678)
price = exchange.price_to_precision('BTC/KRW', 90123456.789)

Nothing is hidden — the implicit API

Alongside the 18 unified capabilities, all 63 endpoints in CCXT's Coinone API block are generated as callable implicit methods, with signing and rate limiting applied:

response = exchange.v2PublicGetChartQuoteCurrencyTargetCurrency({
    'quote_currency': 'KRW', 'target_currency': 'BTC', 'interval': '1h',
})

Browse them on the Coinone implicit API page.

What the raw Coinone API does better

Honest advantages of going direct:

  • The WebSocket surface is wider than CCXT's. Coinone documents six public channels — TICKER, CANDLE, ORDERBOOK, ORDERBOOK_V2, TRADE and BOOKTICKER — plus private WebSocket support. CCXT implements three watch* methods for Coinone: order book, ticker and trades. Streaming candles, book-ticker or private order updates means the raw socket.
  • Market orders are not available through CCXT here. ccxt.coinone.create_order() accepts 'limit' only and raises for anything else. If you need a different order type Coinone supports, you are calling the endpoint directly either way.
  • Candles are not a unified method here. CCXT's Coinone class does not implement fetchOHLCV; the chart endpoint is reachable only through the implicit API, without unified OHLCV parsing. If your workload is candle-heavy, calling the endpoint directly is less indirect.
  • The documentation is the source of truth, in Korean. Coinone's developer centre is written against the raw fields. Debugging a direct call means reading one document; debugging through CCXT means mapping unified names back to it first.
  • Zero dependencies. A signed requests call is about twenty lines. For a single-venue script that reads a price and places an order, that may genuinely be all you need.

If Coinone is your only venue and you need its streaming channels or chart endpoint more than you need portability, going direct is a reasonable choice.

Migrating from the raw Coinone API to CCXT

What you are doingRaw Coinone APICCXT
Symbolsquote_currency + target_currency'BTC/KRW'
MarketsGET /public/v2/markets/{quote_currency}load_markets()
TickerGET /public/v2/ticker_new/{quote}/{target}fetch_ticker() / fetch_tickers()
Order bookGET /public/v2/orderbook/{quote}/{target}fetch_order_book()
Recent tradesGET /public/v2/trades/{quote}/{target}fetch_trades()
CurrenciesGET /public/v2/currenciesfetch_currencies()
New orderPOST /order/limit_buy or POST /v2.1/order/limitcreate_order() (limit only)
Cancel orderPOST /v2.1/order/cancelcancel_order()
Open ordersPOST /v2.1/order/open_ordersfetch_open_orders()
BalancePOST /v2.1/account/balance/allfetch_balance()
Deposit addressPOST /v2/account/deposit_addressfetch_deposit_addresses()
Streamswss://public-ws-api.coinone.co.krwatch_* on ccxt.pro.coinone
Anything not listedthe endpoint URLthe same endpoint as an implicit method

FAQ

Does Coinone have an official Python or Node SDK? Not one it publishes. The Coinone developer centre at docs.coinone.co.kr documents the REST and WebSocket APIs directly and does not point to a client library in any language. If you want a maintained client, CCXT is the practical option.

Does CCXT support Coinone over WebSocket? Partly. CCXT implements three watch* methods for Coinone — watchOrderBook, watchTicker and watchTrades. Coinone's own public socket also carries candle, book-ticker and private channels, which CCXT does not wrap; reach those with the raw socket if you need them.

How does Coinone's private API authenticate? It signs a base64-encoded JSON payload. The payload is sent both as the request body and in the X-COINONE-PAYLOAD header, and X-COINONE-SIGNATURE is an HMAC-SHA512 of that base64 string. The nonce is a UUID v4 on v2.1 and an increasing millisecond timestamp on v2.0. Keys are also bound to an IP allowlist of up to five IPv4 addresses. CCXT implements all of this in its coinone signer.

Is there a Coinone testnet I can use with setSandboxMode? No. CCXT's Coinone class declares no sandbox because Coinone does not publish testnet base URLs.

Can I still call Coinone-specific endpoints through CCXT? Yes — all 63 endpoints in the class's API block, across the legacy, v2.0 and v2.1 groups, are generated as implicit methods with signing and throttling applied.

Is CCXT free? Yes. MIT-licensed, including the WebSocket support.

Next steps

On this page