Typed SDKs
First-class TypeScript and Python clients. The MCP server and every example are built on them. Same resources, same shapes, in both languages — pick a language once below and every example on the site follows.
TypeScript — @multidex/sdk
pnpm add @multidex/sdk # or npm i / yarn addZero runtime dependencies (native fetch, Node ≥ 20 or any modern browser). Config resolves from arguments, then the MULTI_API_KEY / MULTI_BASE_URL env vars.
Python — multidex-sdk
pip install multidex-sdkSync and async clients (built on httpx), pydantic models, and SSE streaming.
Create a client
import { MultiClient } from "@multidex/sdk";
const multi = new MultiClient({ apiKey: process.env.MULTI_API_KEY });Market data
Orderbooks, candles, and smart-routing recommendations. No key needed.
const book = await multi.markets.getOrderbook("BTC");
const candles = await multi.charts.getCandles("BTC", { interval: "1h", limit: 200 });
const routing = await multi.routing.getRecommendation("BTC", { side: "BUY" });Streaming
Live trades, orderbook updates, and candles over SSE. TypeScript streams are async generators (stop with break or an AbortSignal); Python streams are iterators.
const ac = new AbortController();
for await (const ev of multi.streams.trades("BTC", { signal: ac.signal })) {
console.log(ev.event, ev.data); // event: "trade"
}Async (Python)
The async client mirrors the sync surface — same resources, awaited.
import asyncio
from multidex import AsyncMultiClient
async def main():
async with AsyncMultiClient() as v:
print(await v.markets.get_markets())
async for ev in v.streams.candles("BTC", interval="1m"):
print(ev.data); break
asyncio.run(main())Execution
Execution needs an agent key with the orders:execute scope. Pass an idempotency key so retries never double-submit.
Place a smart-routed order
const order = await multi.orders.place(
{ symbol: "BTC", side: "BUY", type: "MARKET", quantity: "0.01" },
{ idempotencyKey: crypto.randomUUID() },
);Open a position with a TP/SL bracket
takeProfitPrice / stopLossPrice are optional: after the entry fills, the server places a reduce-only TP (limit) and SL (stop-market) on the same venue.
const opened = await multi.orders.openPosition(
{
symbol: "BTC", direction: "LONG", size: "0.01",
leverage: 3, orderType: "MARKET",
takeProfitPrice: "75000", stopLossPrice: "60000",
},
{ idempotencyKey: crypto.randomUUID() },
);Batch orders
Up to 10 orders in one call, executed sequentially with per-order policy checks.
const batch = await multi.orders.placeBatch([
{ symbol: "BTC", side: "BUY", type: "MARKET", quantity: "0.01" },
{ symbol: "ETH", side: "SELL", type: "LIMIT", quantity: "0.5", price: "4200" },
]);Portfolio
One-call cross-venue portfolio: positions, PnL, funding, risk summary.
const portfolio = await multi.portfolio.get();
console.log(portfolio.summary.totalUnrealizedPnlUsd, portfolio.summary.highestRisk);Execution strategies
Server-run execution algos: twap (timed market slices) and scaled (a ladder of limit orders across a price range, via priceLow / priceHigh).
// TWAP: timed market slices
const twap = await multi.strategies.create({
kind: "twap", symbol: "BTC", side: "BUY", totalQuantity: "0.1",
sliceCount: 10, intervalMs: 60_000,
});
// Scaled ladder: limit orders spread across a price range
const scaled = await multi.strategies.create({
kind: "scaled", symbol: "ETH", side: "BUY", totalQuantity: "1",
sliceCount: 5, priceLow: "3800", priceHigh: "4000",
});
await multi.strategies.cancel(twap.id);Pair trades
Long one asset, short another, in a single position.
const pair = await multi.pairs.open({ longSymbol: "ETH", shortSymbol: "BTC", notionalUsd: 500, leverage: 3 });
await multi.pairs.close(pair.id!);Cross-chain swaps
Quote → prepare (sign yourself, non-custodial) or execute (headless). Amounts are in the smallest unit (wei / lamports).
const quote = await multi.swaps.quote({
fromChain: 42161, toChain: 8453,
fromToken: "0x0...", toToken: "0x833...",
fromAmount: "1000000000000000",
});
const prepared = await multi.swaps.prepare({ quoteId: quote.bestQuote?.quoteId });Error handling
Every failure throws/raises a typed error you can narrow: MultiAuthError (401/403), MultiRateLimitError (429, carries the retry-after delay), MultiAPIError, MultiTimeoutError, MultiNetworkError.
import { MultiAuthError, MultiRateLimitError } from "@multidex/sdk";
try {
await multi.orders.place({ symbol: "BTC", side: "BUY", type: "MARKET", quantity: "0.01" });
} catch (err) {
if (err instanceof MultiRateLimitError) {
// err.retryAfterMs → back off, then retry with the same idempotency key
} else if (err instanceof MultiAuthError) {
// bad/expired key, or missing scope
} else {
throw err;
}
}markets, trades, charts, routing, streams, orders, positions, portfolio, strategies, pairs, swaps.Parity
- —Both SDKs default to
https://multi-venym-labs.vercel.app/api; streams target the backend directly to avoid proxy buffering. - —Both unwrap the
{ success, data }envelope and pass through raw array endpoints (candles). - —Both retry idempotent GETs on 429/5xx with backoff, honoring
Retry-After.