Bot API
Exchange-style trading API for Hummingbot, ccxt, Freqtrade and your own code: key and secret, HMAC headers, JSON orders, one account per key.
The Bot API is the simple lane. You get a key id and a secret from the Bots tab (side menu, MCP & API, or /bots), you sign each call with HMAC-SHA256 the way every other exchange API works, and TRUE places the order on the venue for you with your trade-only delegate. No Ed25519, no borsh, no venue host. If you want to sign venue orders yourself, the Venue REST lane is unchanged.
Base URL: https://app.truefinance.ai/api/bot/v1
What a key is
A key is bound at creation to one account: your Main Trading account or one manual sub-account. It carries read, or read and trade. Optional controls: an IP allowlist (CIDRs), a per-order notional cap in USD, an open position cap in USD per market (the position plus resting same-direction orders, priced at the mark; orders that reduce exposure always pass), and an expiry (default 90 days, maximum 365). You can pause, resume and revoke a key, and pause every key with one button. The secret is shown once.
With trade it can place, change and cancel orders on the one account it was made for, until you revoke it. It cannot withdraw, deposit, move money between accounts, touch another account, create keys or change settings. There is no such endpoint. Your daily loss limit and the emergency stop under Rules freeze the account at the exchange and stop bot orders too.
Signing
Three headers on every private call:
X-TRUE-KEY: tbk_<16 hex> the key id
X-TRUE-TS: 1760737000000 unix milliseconds
X-TRUE-SIGN: hex( HMAC_SHA256( secret, ts + METHOD + path + rawBody ) )
path is everything after the host, including the query string, for example /api/bot/v1/orders or /api/bot/v1/orders?market=BTCUSDC. rawBody is the exact bytes you send (empty for GET and DELETE without a body). METHOD is upper case. The timestamp must be within 30 seconds of server time (GET /api/bot/v1/time), and the same signature is refused if it is seen twice within 60 seconds.
import hmac, hashlib, time, json, requests
HOST, KEY, SECRET = "https://app.truefinance.ai", "tbk_...", "tbs_..."
def call(method, path, body=None):
raw = json.dumps(body, separators=(",", ":")) if body is not None else ""
ts = str(int(time.time() * 1000))
sign = hmac.new(SECRET.encode(), (ts + method + path + raw).encode(), hashlib.sha256).hexdigest()
h = {"X-TRUE-KEY": KEY, "X-TRUE-TS": ts, "X-TRUE-SIGN": sign, "Content-Type": "application/json"}
return requests.request(method, HOST + path, headers=h, data=raw or None, timeout=20).json()
call("GET", "/api/bot/v1/account")
call("POST", "/api/bot/v1/orders", {"market": "BTCUSDC", "side": "buy", "type": "limit", "size": "0.001", "price": "50000", "postOnly": True})
Errors come back as {"ok": false, "error": "<code>", "message": "<what to do>"}. The codes a first attempt hits: missing_auth_headers, timestamp_out_of_window, bad_signature, ip_not_allowed, insufficient_scope, key_paused, rate_limited.
Public, no signature
GET /api/bot/v1/time { "serverTime": 1760737000000 }
GET /api/bot/v1/markets Every listed perp market as a bot needs it: symbol, base, quote, active, lotSize, tickSize, minOrderNotionalUsd, maxOrderSize, maxPositionSize, maxLeverage (the client cap you may use), makerFeePct, takerFeePct, fundingRate8h, priceBands.
GET /api/bot/v1/ticker?market=BTCUSDC 24 h stats from the venue (market=ALL for all). last_traded_price is the last fill, not the mark.
GET /api/bot/v1/mark?market=BTCUSDC The oracle mark, which is the settlement and liquidation price. Mark to market on this, never on the last fill.
GET /api/bot/v1/bbo?market=BTCUSDC GET /api/bot/v1/orderbook?market=BTCUSDC&depth=50 GET /api/bot/v1/candles?market=BTCUSDC&interval=1h&limit=300&startTime=<ms>&endTime=<ms> Candles are the index series the mark follows (Binance spot of the base). Intervals 1m to 1M, up to 1000 rows, [openTime, open, high, low, close, volume], oldest first. Crypto markets only; stocks, FX and commodities answer no_candles.
GET /api/bot/v1/trades?market=BTCUSDC&limit=100&startAt=<ms> Venue reads proxied as { "ok": true, "data": ... }, same field names as the market data pages.
Private reads (read)
GET /api/bot/v1/key The key’s own policy, status and counters. Call it first: it proves your signing works.
GET /api/bot/v1/account { "ok": true, "accountKey": "...", "account": { "equityUsd": 1032.4, "freeCollateralUsd": 812.1, "unrealizedPnlUsd": 12.3, "raw": {} },
"balances": {}, "positions": [], "openOrders": [], "leverages": { "BTCUSDC": 10 }, "marginType": "cross" }
GET /api/bot/v1/positions?market=BTCUSDC GET /api/bot/v1/orders?market=BTCUSDC GET /api/bot/v1/orders/history?market=BTCUSDC&status=FILLED&startAt=<ms>&endAt=<ms> GET /api/bot/v1/orders/{id}?market=BTCUSDC One order by venue id, resting or settled, with one normalized status: open, partially_filled, filled, cancelled, rejected, expired. 404 order_not_found when the id is not on this account. ?market= narrows the history read and makes the call cheaper.
GET /api/bot/v1/fills?market=BTCUSDC&limit=100&cursor=<cursor>&startAt=<ms> GET /api/bot/v1/positions/history?market=BTCUSDC&limit=50&cursor=<cursor> Positions, open orders, terminal orders (with cancel_reason), your fill tape and closed positions, all for the key’s account only. Field names are the venue’s own.
GET /api/bot/v1/stream Server-sent events: ready, then account whenever positions, open orders or equity change (checked every 2 s), heartbeat every 15 s, bye after 30 minutes (reconnect). Sign the GET like any other call.
POST /api/bot/v1/ws-token A one-hour token for the market data WebSocket wss://app.truefinance.ai/api/ws/perps (order book deltas, trades, bbo, mark). Send it as Authorization: Bearer <token> on the upgrade or as ?token=. Topics and framing: Market data WebSocket.
Trading (trade)
POST /api/bot/v1/orders { "market": "BTCUSDC", "side": "buy", "type": "limit", "size": "0.001", "price": "50000",
"tif": "GTC", "postOnly": true, "reduceOnly": false, "leverage": 5, "clientOrderId": "my-id-42" }
| Field | Values |
|---|---|
side | buy or sell (long / short accepted) |
type | market (default tif IOC), limit (default GTC), stop_market and take_profit_market with triggerPrice |
size | base amount as a string, floored to the market’s lot size for you |
tif | GTC, IOC, FOK |
leverage | optional, 1 to the market’s client cap; set once per market, the venue remembers it |
clientOrderId | optional, up to 64 chars; the same id within 24 h returns the first answer with duplicate: true instead of a second order |
201 with { "ok": true, "order": { ... } }. A refusal is 400 or 403 with the venue’s or the guard’s reason (order_notional_cap, position_notional_cap, leverage_out_of_range, slippage guard, price band, insufficient margin). The same lot alignment, market-order slippage guard, leverage caps and risk rules the TRUE app applies to a human order apply here.
DELETE /api/bot/v1/orders/{orderId} DELETE /api/bot/v1/orders?market=BTCUSDC Cancel one order, or every open order (optionally one market, at most 100 per call).
PATCH /api/bot/v1/orders/{orderId} { "price": "50100" } edits a resting limit order in place; the order id stays.
POST /api/bot/v1/leverage { "market": "BTCUSDC", "leverage": 10 }. Refused above the client cap with maxLeverage in the answer.
Treat a write with no 2xx within 5 seconds as unknown. Read GET /api/bot/v1/orders before retrying, or send the retry with the same clientOrderId, which cannot double an order.
Limits
600 reads and 120 trading calls a minute per key (X-RateLimit-Remaining on every answer, 429 with Retry-After beyond). Ten keys per user.
Hummingbot
The connector has been submitted upstream to Hummingbot (pull request in review); until it ships in a Hummingbot release, use the TRUE fork below. A ready connector, true_perpetual, plugs TRUE into Hummingbot: market making, directional scripts and V2 controllers run against TRUE perps with a bot key and nothing else.
- Create a key in the Bots tab with scope read + trade.
- Get Hummingbot with the connector inside:
git clone https://github.com/True-AI-Labs/hummingbot.git(the TRUE fork, connector onmaster), then the standard Hummingbot install (./install,conda activate hummingbot,./compile) ordocker build -t hummingbot-true .from that checkout. On a stock Hummingbot checkout,install.shfrom the connector package copiestrue_perpetual/intohummingbot/connector/derivative/. The connector is pure Python; nothing to compile. - In Hummingbot:
connect true_perpetual, paste the key id and the secret,balance, thencreateaperpetual_market_makingstrategy onBTC-USDCandstart.
Pairs are written with a dash (BTC-USDC, ETH-USDC). Supported: LIMIT, MARKET, LIMIT_MAKER (post only), one position per market (ONEWAY), USDC collateral, leverage per market, websocket order book, trades and mark price, balances and positions pushed from the account stream, fills and order states polled every 5 seconds. The connector throttles itself to the key’s limits and syncs its clock to GET /time.
ccxt
The truefinance exchange has been submitted upstream to ccxt (pull request in review); until it ships in a ccxt release, a drop-in class is in the connector package. A ccxt exchange class, truefinance (sync) and truefinance_async, ships as one file in the connector package: from truefinance import truefinance; ex = truefinance({"apiKey": "tbk_...", "secret": "tbs_..."}), then the usual load_markets, fetch_balance, fetch_positions, create_order("BTC/USDC:USDC", "limit", "buy", 0.001, 60000, {"postOnly": True}), fetch_order, edit_order, cancel_order, fetch_my_trades, set_leverage. Symbols use ccxt’s swap form BTC/USDC:USDC; errors map to ccxt’s exception classes.
Freqtrade
Freqtrade runs on ccxt, so the same class carries it: the connector package has a launcher, python -m freqtrade_truefinance <freqtrade arguments>, that registers TRUE with ccxt and adds the Freqtrade exchange subclass (futures, cross margin, leverage from the market list). Use config.truefinance.example.json from the package as the config: trading_mode futures, margin_mode cross, pairs like BTC/USDC:USDC. Candle strategies work on crypto markets through GET /candles. Funding payments are settled at the venue into the position and are not modelled by Freqtrade; stoploss stays on Freqtrade’s side.
Other connectors
The shapes above are deliberately the common exchange vocabulary so a custom connector maps one to one: fetchMarkets to /markets, fetchBalance and fetchPositions to /account, createOrder to POST /orders, cancelOrder to DELETE /orders/{id}, fetchOrder to GET /orders/{id}, fetchMyTrades to /fills, fetchOpenOrders to /orders. A reference Python client (true_bot.py) ships with the connector package.