Getting started
MNX is a derivatives exchange running on the MegaETH blockchain. It lists three kinds of markets: perpetual swaps (contracts that track an asset's price and never expire), binary futures (YES/NO prediction markets priced from 0 to 1 as a probability), and numeric futures (markets on an unbounded numeric outcome). Everything a trading client needs — market data, order placement, balances, positions — is exposed over a REST API plus a WebSocket feed.
Base URLs
- Production:
https://api.app.mnx.fi - Testnet:
https://api.testnet.mnx.fi
Production runs on MegaETH mainnet with real funds. Testnet runs on the MegaETH testnet and is the right place to develop and test an integration. Both environments serve the same API surface.
The /v0/ prefix
Every public endpoint is served under the /v0/ path prefix. The API reference writes paths with the prefix included, e.g. /v0/markets or /v0/orders. GET endpoints take parameters as query parameters (and path segments); POST and PUT endpoints take a JSON body with Content-Type: application/json.
Your first call
Most market-data endpoints require no authentication. List markets on testnet:
curl "https://api.testnet.mnx.fi/v0/markets?type=perpetual&limit=3"Each market object includes a stable numeric market_id (the canonical identifier — use it for orders and WebSocket topics), a human-readable symbol, current prices, and the market contract addresses. From there you can drill into public data:
GET /v0/markets/:market_id— one market's full detailGET /v0/markets/stats— 24h volume, open interest, mark price, and funding rate per marketGET /v0/orderbook/by-market/:market_id— aggregated order book levelsGET /v0/markets/:market_id/candlesticks— OHLC (open/high/low/close) candles by interval
Public order data
For public order book data, use GET /v0/orderbook/by-market/:market_id or GET /v0/orderbook?symbol=.... These return aggregated price levels: the market identifier route uses long_levels and short_levels; the symbol route uses bids and asks.
Each level contains only side, price, price_e18_raw, quantity, quantity_e18_raw, and order_count. Quantities are totals at that price and side. Pending trigger orders are excluded, along with account identities, signatures, margin details, and individual order metadata.
To read your own orders, use the authenticated GET /v0/orders/by-user/:user_id endpoint with an optional market_id query parameter, or fetch one order with GET /v0/orders/:order_id.
Placing orders and reading account data requires authentication and an order signature — see Authentication & signing and Placing orders.
Response conventions
| Case | What you get |
|---|---|
| Success | HTTP 200 with the documented response object as the JSON body — there is no wrapper envelope. Endpoints with no payload return { "success": true }. |
| Error | An HTTP error status (400, 401, 403, 404, 408, 409, 429, 500, 503) with a JSON body of { "message": "...", "details": ... }, where details is optional structured context (validation errors list the offending field). |
Raw integer amounts
Fields whose names end in _e18_raw are exact integers scaled by 1018, serialized as strings to avoid floating point rounding (they match the on-chain representation). For example, a balance of 1.5 USDM appears as "1500000000000000000". Many responses include both the raw field and a human-readable companion; when doing math on raw values, parse them as arbitrary-precision integers, never as floats.
Read-after-write bookmarks
The exchange's read endpoints are served from a read model that trails writes by a short interval. Mutating responses (order placement, cancels) may include a read_after object; you can pass its values back as read_after_shard_id and read_after_shard_seq query parameters on GET endpoints that accept them. The read then waits until the read model reflects your write, giving read-your-own-writes consistency. Omitting the parameters returns the latest available data immediately.
The server decides how long to wait — up to 5 seconds — so there is no client-supplied timeout. If the wait does not finish in time you get an answer describing what to do next, and a client that treats these as fatal will stop on a read that was only going to need one more try:
- 409 with
"code": "HTV1_READ_AFTER_STALE_BOOKMARK"— the bookmark cannot be placed at all (unknown owner, or a rebuilt read model). Discard it and read again without it. - 503 with
"code": "HTV1_READ_AFTER_PENDING"and aRetry-Afterheader — the write has not landed in the read model yet. Keep the bookmark and ask again after the requested delay. - 503 with
"code": "HTV1_READ_AFTER_UNAVAILABLE"— this process cannot establish projection freshness. Keep the bookmark and retry within your client's bounded read budget after theRetry-Afterdelay (currently 2 seconds). - 429 with
"code": "HTV1_READ_AFTER_CAPACITY"— the API is already holding as many waiting reads as it allows. Back off, then retry with the same bookmark.