CCXT

CCXT vs the raw NDAX API

NDAX publishes no official SDK — its AlphaPoint-based API is documentation only. Compare hand-rolling nonce/HMAC auth, OMS ids and 2FA against CCXT's ndax class.

NDAX is a Canadian exchange with CAD and USDT markets. Its API is documented at apidoc.ndax.io, which describes version 3.3 of the NDAX Exchange software — an AlphaPoint deployment, with the AlphaPoint conventions that come with it: an OMS id required on nearly every call, a WebSocket-first frame format, and an authentication handshake that can end in a session token rather than a per-request signature.

NDAX does not publish a client library. The repositories under its GitHub organisation are documentation — NDAXlO/ndax-api-documentation is a two-commit reference for the WebSocket API with no code in it — and neither the API reference nor those repositories link to an official SDK in any language. The only third-party client this comparison found is RobJohnston/Ndax.Api, an unaffiliated .NET Standard library, MIT-licensed, with no stars and 18 commits.

So the honest comparison here is not CCXT against a vendor SDK. It is CCXT against the code you would write yourself.

TL;DR

  • Write it yourself if you need one or two NDAX endpoints, in a language CCXT does not cover, and you are comfortable owning the auth handshake and the OMS-id plumbing forever.
  • Pick CCXT if you want NDAX with 36 unified capabilities, four watch* streaming methods, a rate limiter, typed errors and testnet support — in TypeScript, JavaScript, Python, PHP, C#/.NET, Go or Java.
  • Choosing CCXT does not hide the raw API. All 104 NDAX endpoints are generated as implicit methods, signed and rate-limited, so anything the unified API does not model is still one call away.

At a glance

CCXTRaw NDAX API
Exchanges covered104 (NDAX is one of them)NDAX only
Official client libraryn/anone published
LanguagesTypeScript, JavaScript, Python, PHP, C#/.NET, Go, Java — one APIwhatever you write; one unaffiliated .NET library exists
Unified market data + trading APIyes — same method names across every exchangeno — AlphaPoint request/response shapes
Unified capabilities implemented36 for ndax, of which 19 are fetch*n/a
Symbols'BTC/CAD', 'BTC/USDT'numeric InstrumentId, plus OMSId
Authenticationhandled — nonce + HMAC-SHA256, or session token after sign_in()Nonce + UserId + APIKey + Signature headers, or Basic auth then a session token
Two-factor sign-insign_in() handles the Pending2FaToken exchange and TOTPyour code
OMS / account idsinjected automatically (options['omsId'], fetch_accounts())passed by hand on nearly every call
WebSocketsyes — watchOrderBook, watchTrades, watchTicker, watchOHLCVyes, and it is the primary interface — you frame the messages
Raw endpoint accessyes — 104 endpoints as implicit methodsit is all raw
Built-in rate limiteryes, on by default (rateLimit 1000 ms)your code
Unified error typesyes — 41 typed exceptions in one hierarchyHTTP status plus AlphaPoint error payloads
Testnet / stagingset_sandbox_mode(True) swaps in the staging hostswap the base URL yourself
Popularity43.8k GitHub stars · 4.8M PyPI + 494k npm installs/month (one package, every venue)n/a — no package to count
LicenceMITn/a
SupportDiscord, Telegram, GitHub — usually same-dayNDAX support desk

Figures verified September 2026 against CCXT v4.5.77, the NDAX API reference at apidoc.ndax.io, the NDAXlO GitHub repositories and the third-party RobJohnston/Ndax.Api repository.

The same job, written both ways

Fetch a ticker

import ccxt

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

The raw path needs the OMS id and a numeric instrument id before it can ask for anything. CCXT resolves both during load_markets() and returns a unified ticker structure — the same keys, types and units you get from Kraken or Coinbase.

Place a limit order

import ccxt

exchange = ccxt.ndax({
    'apiKey': '...',
    'secret': '...',
    'uid': '...',        # UserId
})
order = exchange.create_order('BTC/CAD', 'limit', 'buy', 0.001, 80000)
print(order['id'], order['status'])

CCXT implements exactly that signing scheme — hmac(nonce + uid + apiKey, secret, sha256) in the Nonce / APIKey / Signature / UserId headers — plus the enum tables (Side, OrderType, TimeInForce), the OMS id, and the account-id lookup. The AccountId in particular is not a constant: CCXT calls fetch_accounts() once and uses the first account unless you override it with options['accountId'] or a per-call param.

Stream an order book

import ccxt.pro
import asyncio

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

asyncio.run(main())

The raw frame format is doubly encoded — the payload is a JSON string inside a JSON object — and Level 2 updates arrive as positional arrays whose meaning you look up in the reference. NDAX's docs also specify client-generated sequence numbers, recommending even numbers starting at 2.

CCXT implements four streaming methods for ndax: watchOrderBook, watchTrades, watchTicker and watchOHLCV. watch_order_book returns a live, merged book — snapshot and deltas aligned, gaps detected, cache bounded — in the same order book structure as fetch_order_book, with reconnection and resubscription handled underneath. There are no private (order, balance) streams for ndax in CCXT.

Where the differences actually bite

The sign-in handshake

NDAX supports two authentication paths, and CCXT implements both. The per-request path is the nonce/HMAC one above. The session path is sign_in(): Basic auth with login:password, which may come back with Requires2FA and a Pending2FaToken; CCXT then computes a TOTP code from twofa, posts it with the pending token, stores the resulting session token, and sends it as APToken on subsequent requests.

exchange = ccxt.ndax({
    'apiKey': '...', 'secret': '...', 'uid': '...',
    'login': '...', 'password': '...', 'twofa': '...',
})
exchange.sign_in()

That is a stateful, three-step, TOTP-bearing flow. It is the kind of thing that works on the first day and breaks quietly six months later when the token expires in a way you did not model.

Rate limits you do not have to model

CCXT ships a token-bucket throttler that is on by default (enableRateLimit = true, rateLimit = 1000 ms for ndax). You call methods in a loop and the library paces them. On the raw path, pacing and back-off are code you write.

One error hierarchy

CCXT's typed exception tree has 41 classes descending from BaseError. For NDAX it maps the venue's own strings — Not_Enough_Funds to InsufficientFunds, Resource Not Found to OrderNotFound, Invalid InstrumentId to BadSymbol, the 2FA-required message to AuthenticationError — on top of the base HTTP mapping, where a 429 becomes RateLimitExceeded, a 401 becomes AuthenticationError and transport failures become NetworkError subclasses. You write except ccxt.InsufficientFunds once and it keeps working when you add a second exchange, instead of matching on errorcode: 101 and hoping the string never changes.

Precision, rounding and string math

load_markets() pulls NDAX's tick and step sizes and exposes them through amount_to_precision, price_to_precision and cost_to_precision, backed by the Precise string-arithmetic class so quantities never drift through float rounding into a rejected order:

amount = exchange.amount_to_precision('BTC/CAD', 0.0012345678)
price = exchange.price_to_precision('BTC/CAD', 81234.56789)

Seven languages, one API

CCXT is written once in TypeScript and transpiled to JavaScript, Python, PHP, C#/.NET, Go and Java, with identical method names and return structures in every one. On the raw path, each language is a fresh implementation of the same handshake.

import ccxt
exchange = ccxt.ndax()
ticker = exchange.fetch_ticker('BTC/CAD')

Staging without a second code path

NDAX runs a staging environment on a separate host. CCXT has it wired to the standard switch:

exchange = ccxt.ndax({'apiKey': '...', 'secret': '...', 'uid': '...'})
exchange.set_sandbox_mode(True)   # swaps in the staging REST and WebSocket URLs

Nothing is hidden — the implicit API

Alongside the 36 unified capabilities, all 104 NDAX endpoints are generated as callable implicit methods, with signing, rate-limit accounting and error mapping applied:

# any raw NDAX endpoint, camelCased from its path
response = exchange.private_get_get_account_positions({
    'OMSId': 1, 'AccountId': 1})

Browse them on the ndax implicit API page.

What writing it yourself does better

An honest list — hand-rolling is not always the wrong call:

  • The WebSocket API is the whole API. AlphaPoint exposes essentially every function over the socket, including private ones. CCXT's ndax uses the socket for four public market-data methods and REST for everything else, so private streams — order and balance events — are simply not available through CCXT for this venue. If you need them, you write the client.
  • The docs map one-to-one onto raw frames. Reading apidoc.ndax.io while debugging your own client is a direct correspondence: SendOrder is SendOrder. CCXT's unified names are a deliberate abstraction, which is an extra hop.
  • A far smaller dependency. If you need three endpoints, sixty lines of requests code is a smaller install and a smaller attack surface than a library covering 104 exchanges.
  • Any language you like. CCXT ships seven. If your service is in Rust, Elixir or Swift, the raw API is the only route — and RobJohnston/Ndax.Api shows that a focused single-venue client is a tractable weekend project.
  • AlphaPoint knowledge transfers. The same frame format and OMS conventions appear across other AlphaPoint deployments, so a client you write is partly reusable at other white-label venues.

If NDAX is your only venue, you need its private WebSocket events, and you are in a language CCXT does not ship, writing your own client is the right answer.

Migrating from a hand-rolled NDAX client to CCXT

What you are doingRaw NDAX APICCXT
Symbolsnumeric InstrumentId + OMSId'BTC/CAD'
Clientyour signing helperccxt.ndax({'apiKey': ..., 'secret': ..., 'uid': ...})
InstrumentsGetInstrumentsload_markets()
TickerGetLevel1fetch_ticker()
Order bookGetL2Snapshotfetch_order_book()
CandlesGetTickerHistoryfetch_ohlcv()
Public tradesGetLastTradesfetch_trades()
AccountsGetUserAccountsfetch_accounts()
BalanceGetAccountPositionsfetch_balance()
New orderSendOrdercreate_order()
Modify orderModifyOrderedit_order()
Cancel orderCancelOrdercancel_order()
Open ordersGetOpenOrdersfetch_open_orders()
Own tradesGetTradesHistoryfetch_my_trades()
StreamsSubscribeLevel2 etc. in AlphaPoint frameswatch_* on ccxt.pro.ndax
Stagingswap the base URLset_sandbox_mode(True)
Anything not listedraw callthe same endpoint as an implicit method

Start with Install, then the Manual, then the ndax unified API reference.

FAQ

Does NDAX have an official API SDK? No. NDAX publishes API documentation at apidoc.ndax.io and a documentation-only GitHub repository for the WebSocket API, but no client library in any language. The only third-party client this page found is an unaffiliated .NET Standard project, RobJohnston/Ndax.Api.

What credentials does CCXT need for NDAX? apiKey, secret and uid (your NDAX UserId) for the per-request HMAC path. If you want the session-token path, also supply login, password and twofa, then call exchange.sign_in() — CCXT handles the Pending2FaToken exchange and computes the TOTP code.

Do I have to pass OMSId and AccountId myself in CCXT? No. CCXT sets OMSId from options['omsId'] (default 1) and resolves AccountId by calling fetch_accounts() once, using the first account. You can override either with options or a per-call param.

Does CCXT support NDAX WebSockets? Yes, for public market data — watchOrderBook, watchTrades, watchTicker and watchOHLCV. Private order and balance streams are not implemented for this venue, even though the underlying AlphaPoint socket exposes them.

Can I test NDAX against a staging environment? Yes. exchange.set_sandbox_mode(True) swaps in NDAX's staging REST and WebSocket hosts in one call, with no second code path.

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

Next steps

On this page