CCXT

CCXT vs the ApeX Omni API and apexpro-openapi

CCXT compared with ApeX's official Python connector on zk order signing, language coverage, streaming, testnet and unified structures for the Omni perpetuals DEX.

ApeX Omni is a perpetuals DEX whose private endpoints are signed twice: an API key, secret and passphrase authenticate the HTTP request, and a separate zk key signs the order payload itself. That second signature is the part that makes a hand-rolled integration awkward, and it is the reason ApeX publishes apexpro-openapi — the "Official Python3 API connector for Apex omni's HTTP and WebSockets APIs", MIT-licensed, 40 GitHub stars, on PyPI as apexomni.

Both libraries produce the same signature. The question that decides between them: do you need ApeX account onboarding, or do you need ApeX to look like every other venue in your system?

TL;DR

  • Pick apexpro-openapi if you need to create an Omni account programmatically — derive zk keys from an Ethereum private key, register the user, rotate the public key — or you want method names that match ApeX's own v3 reference exactly, and Python is your only language.
  • Pick CCXT if the account already exists and you want ApeX perpetuals to behave like the other venues in your book: create_order, fetch_positions, watch_order_book, the same in TypeScript, Python, PHP, C#, Go and Java.
  • CCXT signs the zk payload itself. It is not a thin REST wrapper that leaves the hard part to you — supply the Omni seeds once and order placement, transfers and withdrawals are signed by the library.

At a glance

CCXTapexpro-openapi
Venues covered104 (ApeX is one of them)ApeX only
LanguagesTypeScript, JavaScript, Python, PHP, C#/.NET, Go, Java — one APIPython 3.9–3.12
Packages to install1 (ccxt)1 (apexomni)
Unified market data + trading APIyes — 43 capabilities on apexno — ApeX's own *_v3 method and field names
zk order signingyes, built in — supply options['seeds']yes, built in
Account onboardingno — bring existing credentialsyes — derive_zk_key, register_user_v3, change_pub_key_v3
WebSocketsyes — 10 watch* methodsyes — WebSocket with per-channel callbacks
Raw endpoint accessyes — 27 ApeX endpoints as implicit methodsyes, it is the whole product
Built-in rate limiteryes, on by default (rateLimit 20 ms)not covered in the README
Unified error typesyes — 41 typed exceptions in one hierarchyApeX response codes
Testnetexchange.set_sandbox_mode(True) swaps in testnet.omni.apex.exchangeAPEX_OMNI_HTTP_TEST constant
Popularity43.8k GitHub stars · 4.8M PyPI + 494k npm installs/month (one package, every venue)40 GitHub stars · 1.4k PyPI installs/month (apexomni)
LicenceMITMIT
SupportDiscord, Telegram, GitHub — usually same-dayGitHub issues

Figures verified September 2026 against CCXT v4.5.77, the ApeX-Protocol/apexpro-openapi repository and its README_V3.md, and install counts from PyPI and npm.

The same job, written both ways

Fetch a ticker

import ccxt

exchange = ccxt.apex()
ticker = exchange.fetch_ticker('BTC/USDT:USDT')
print(ticker['last'], ticker['baseVolume'])

The connector requires a configs_v3() call before most other work — it carries the symbol configuration the SDK signs against. CCXT does the equivalent inside load_markets(), which every unified method calls for you, and returns a unified ticker structure with the same keys you get on Binance or Hyperliquid.

Place an order

import ccxt

exchange = ccxt.apex({
    'apiKey': '...',
    'secret': '...',
    'password': '...',                 # the API passphrase
    'options': {'seeds': '...'},       # Omni zk seeds, from API Management > Omni Key
})
order = exchange.create_order('BTC/USDT:USDT', 'limit', 'buy', 0.01, 65000)
print(order['id'], order['status'])

Both sign the order with the zk seeds. The difference is what surrounds it: CCXT loads the market configuration, rounds size and price to the symbol's tick and step, builds the signed payload and returns a unified order structure. With the connector you fetch configs, fetch the account, assemble the parameters ApeX expects — including a timestamp in seconds — and parse the response yourself.

Stream an order book

import ccxt.pro
import asyncio

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

asyncio.run(main())

The connector is callback-shaped: register handlers, then block. CCXT is await-shaped: watch_order_book returns the same structure fetch_order_book returns, so a polling loop becomes a streaming loop by changing one word, and the decision code that reacts to the book stays in the same function as the order that follows it.

CCXT also maintains the book rather than handing you deltas — snapshot alignment, buffered updates during the snapshot fetch, gap detection and re-sync, reconnect and resubscribe, and a bounded depth-limited cache. Those are the same code paths it uses on every other venue, so they are exercised constantly.

Where the differences actually bite

Seven languages, one API

apexpro-openapi is Python only, versions 3.9 to 3.12. If your execution service is Go, C# or Java, ApeX is a from-scratch integration including the zk signature. CCXT is written once in TypeScript and transpiled, with identical method names and return structures:

import ccxt
exchange = ccxt.apex()
ticker = exchange.fetch_ticker('BTC/USDT:USDT')

Portability across perp venues

ApeX Omni is one perpetuals book. Most strategies that trade one trade several, and the venue-specific parts — symbol format, signing scheme, position model — are exactly what CCXT normalises:

for exchange_id in ['apex', 'hyperliquid', 'bybit', 'okx']:
    exchange = getattr(ccxt, exchange_id)()
    print(exchange_id, exchange.fetch_ticker('BTC/USDT:USDT')['last'])

Rate limits you do not have to model

CCXT sets rateLimit = 20 ms for apex — the exchange file records the venue's documented allowance of 600 requests per minute — and ships a token-bucket throttler that is on by default. You write a loop; the library paces it.

Precision, rounding and string math

load_markets() reads ApeX's symbol configuration and exposes tick size, step size and minimum order size through amount_to_precision, price_to_precision and cost_to_precision, backed by the Precise string-arithmetic class. On a signed-payload venue this matters more than usual: a size that violates the step is rejected after you have signed it.

amount = exchange.amount_to_precision('BTC/USDT:USDT', 0.0012345678)
price = exchange.price_to_precision('BTC/USDT:USDT', 65432.10987)

One error hierarchy

CCXT maps ApeX's response codes onto a typed exception treeInsufficientFunds, InvalidOrder, OrderNotFound, RateLimitExceeded, AuthenticationError, NetworkError and 35 more, all descending from BaseError. The same except block keeps working when the order goes to a different venue.

Testnet without a second code path

exchange = ccxt.apex({'apiKey': '...', 'secret': '...', 'password': '...'})
exchange.set_sandbox_mode(True)   # swaps in testnet.omni.apex.exchange

One flag swaps every REST and WebSocket URL, rather than importing a different endpoint constant at each call site.

Nothing is hidden — the implicit API

ApeX's API surface is compact. Alongside the 43 unified capabilities, all 27 endpoints are generated as callable implicit methods with signing, rate limiting and error mapping applied:

# any raw ApeX endpoint, camelCased from its path
response = exchange.public_get_v3_symbols()

Browse them on the apex implicit API page.

What apexpro-openapi does better

An honest list, because these are real:

  • Account onboarding. This is the big one. The connector can take an Ethereum private key, derive the zk keys with derive_zk_key(), register an Omni account with register_user_v3() and complete it with change_pub_key_v3(). CCXT expects credentials that already exist — you generate the API key trio and read the Omni seeds from ApeX's own key-management UI. If you are provisioning accounts programmatically, the connector is the only option.
  • Withdrawals and cross-account transfers with zk signing. create_withdrawal_v3(), create_transfer_out_v3() and create_contract_transfer_out_v3() are first-class SDK calls, including fast-withdraw fee handling.
  • Composite take-profit / stop-loss orders. create_order_v3() accepts the full isOpenTpslOrder / slPrice / tpTriggerPrice parameter set in one call, matching ApeX's own semantics.
  • Legacy ApeX Pro and RWA accounts. The connector still carries the v1 and v2 interfaces used by older ApeX Pro accounts, and its example gallery includes RWA account registration, transfer and order flows. CCXT models the current Omni v3 perpetuals API.
  • Field-for-field fidelity. size, timestampSeconds, BTC-USDT — when you are debugging against ApeX's reference, the connector's names are the reference's names. CCXT's unified names are a deliberate abstraction and one more hop.

If you are building ApeX-specific tooling — onboarding, account management, RWA flows — the official connector is the better dependency, and Python is where it lives.

Migrating from apexpro-openapi to CCXT

What you are doingapexpro-openapiCCXT
Symbols"BTC-USDT" / "BTCUSDT"'BTC/USDT:USDT'
ClientHttpPublic / HttpPrivate_v3 / HttpPrivateSignone ccxt.apex({...})
Configurationconfigs_v3()load_markets()
Tickerticker_v3()fetch_ticker() / fetch_tickers()
Order bookdepth_v3()fetch_order_book()
Tradestrades_v3()fetch_trades()
Candlesklines_v3()fetch_ohlcv()
Funding historyhistory_funding_v3()fetch_funding_rate_history()
New ordercreate_order_v3()create_order()
Cancel orderdelete_order_v3()cancel_order()
Cancel alldelete_open_orders_v3()cancel_all_orders()
Open ordersopen_orders_v3()fetch_open_orders()
Order historyhistory_orders_v3()fetch_orders()
Fillsfills_v3()fetch_my_trades()
Balanceget_account_balance_v3()fetch_balance()
Positionsget_account_v3()fetch_positions()
Transferstransfers_v3()fetch_transfers()
StreamsWebSocket + per-channel callbackswatch_* on ccxt.pro.apex
TestnetAPEX_OMNI_HTTP_TESTset_sandbox_mode(True)
Anything not listednative SDK methodthe same endpoint as an implicit method

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

FAQ

Does CCXT handle ApeX's zk order signature? Yes. Set options['seeds'] to the Omni seeds from ApeX's key-management page alongside your apiKey, secret and password, and CCXT builds and signs the zk payload for orders, transfers and withdrawals itself — in all seven languages, not just Python.

Can CCXT register a new ApeX Omni account? No. Onboarding — deriving zk keys from an Ethereum private key, register_user_v3 and change_pub_key_v3 — is only in ApeX's own Python connector. Create the account and the API key there or in the ApeX UI, then use CCXT for trading.

Does CCXT support ApeX WebSockets? Yes. ccxt.pro.apex implements ten streaming methods, including watchOrderBook, watchOrderBookForSymbols, watchTicker, watchTickers, watchTrades, watchOHLCV, watchOrders, watchMyTrades and watchPositions. They return the same structures as their fetch* counterparts.

Is there an ApeX testnet in CCXT? Yes. exchange.set_sandbox_mode(True) swaps every REST and WebSocket URL to testnet.omni.apex.exchange.

Can I still call ApeX-specific endpoints? Yes — all 27 of them, as implicit methods, with authentication, rate limiting and error mapping applied.

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

Next steps

On this page