mm24

API reference

The API is shaped like Binance USD-M Futures: the same paths, the same parameter names, the same error codes. A client written against Binance should need a base URL and a key, and nothing else. Where we differ, it says so below.

Base URL —

getting a key

Create one under account → API keys. You choose the permissions when you create it, and you can restrict it to a list of addresses.

permissionwhat it allows
readYour account, positions, orders, trades and income. Changes nothing.
tradePlace and cancel orders, set leverage and margin type.
transferMove money in and out of the account.

The secret is shown once, at creation. We store a hash of it and cannot show it to you again; if you lose it, revoke the key and create another.

A key cannot create or revoke keys, change your password, or manage your sessions — those need the password behind a browser session. That is deliberate: otherwise a leaked read-only key becomes a trading key in two steps.

signing a request

Every authenticated request carries:

wherenamewhat
headerX-MBX-APIKEYThe key. Not the secret.
parametertimestampMilliseconds since the epoch.
parameterrecvWindowOptional. Milliseconds. Default 5000, maximum 60000.
parametersignatureLowercase hex HMAC-SHA256, under the secret, of everything else. Sent last.

Sign the bytes you send, in the order you send them. The payload is the query string (or the form body) exactly as it goes on the wire, with signature removed — not a re-encoding of a parsed object. If you build the string, sign that same string, and send it unchanged, you cannot get this wrong. If you sign a re-serialisation, a different parameter order or a differently-escaped space will produce a payload you never sent.

GET parameters go in the query string; POST and DELETE parameters go in the query string too.

curl -H "X-MBX-APIKEY: $KEY" \
  "$BASE/fapi/v2/balance?timestamp=$TS&signature=$SIG"

# where
TS=$(date +%s000)
SIG=$(printf 'timestamp=%s' "$TS" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)
import { createHmac } from 'node:crypto';

function signed(params, secret) {
  const q = new URLSearchParams({ ...params, timestamp: Date.now() }).toString();
  return `${q}&signature=${createHmac('sha256', secret).update(q).digest('hex')}`;
}

There is no /fapi/v1/time yet. Binance publishes one and most client libraries call it at startup to measure their clock offset. We do not serve it, so if your clock is more than recvWindow out you will get -1021 on every request. Check your clock first. Raising recvWindow works but widens the window in which a captured request can be replayed.

endpoints

no credential

endpointparameterswhat
GET /fapi/v1/exchangeInfo—The instrument catalogue: symbols, tick size, lot size, minimum notional, rate limits. Read it before you trade; the filters here are the ones that reject orders.
GET /broker/v1/quote/{symbol}—Our current two-sided price. A sidecar, not a Binance path. 503 when we are not quoting that symbol, which is a real state and not an error to retry through.

read

endpointparameterswhat
GET /fapi/v2/account—Balances, margin and open positions in one response.
GET /fapi/v2/balance—Balance per asset.
GET /fapi/v2/positionRisksymbol optionalOpen positions with entry price, liquidation price and leverage.
GET /fapi/v1/ordersymbol required; orderId or origClientOrderIdOne order. -2013 if it does not exist.
GET /fapi/v1/openOrderssymbol optionalOrders still resting. An unlisted symbol is an error, not an empty list.
GET /fapi/v1/allOrderssymbol required; orderId, startTime, endTime, limit optionalOrder history.
GET /fapi/v1/userTradessymbol, startTime, endTime, limit optionalYour fills, with the commission charged on each.
GET /fapi/v1/incomesymbol, incomeType, startTime, endTime, limit optionalEverything that moved your balance other than a fill: funding, commission, realised P&L, transfers.

trade

endpointparameterswhat
POST /fapi/v1/ordersymbol, side, type, quantity required; price, stopPrice, timeInForce, reduceOnly, newClientOrderId as the type needs themPlace an order.
DELETE /fapi/v1/ordersymbol required; orderId or origClientOrderIdCancel one order.
DELETE /fapi/v1/allOpenOrderssymbol requiredCancel every resting order on that symbol. Answers success even when nothing was open.
POST /fapi/v1/leveragesymbol, leverageSet leverage for that symbol. Sticky per symbol. -2019 if lowering it would leave you short of margin.
POST /fapi/v1/marginTypesymbol, marginType = ISOLATED | CROSSEDSet margin mode. -4047 if the symbol has an open position or order; -4046 if it is already that mode.

Send newClientOrderId. Binance has no request-id parameter, so we use yours as one: retrying a placement that timed out with the same id returns the original order instead of opening a second position. Without it, a retry is a new order — which is Binance's behaviour too, and the reason this is worth saying.

transfer

endpointparameterswhat
POST /broker/v1/transferamount > 0, direction = IN | OUT; requestId optionalMove money in or out. A sidecar, not a Binance path. Pass your own requestId to make a retry idempotent.

operator

House endpoints. They need an operate key, which a client account cannot create — listed so the surface is complete, not because you can call them.

endpointwhat
GET /broker/v1/exposureNet exposure per instrument.
GET /broker/v1/report/pnlP&L attribution.
GET /broker/v1/report/riskPer-client risk rows.
GET /broker/v1/venue-marginMargin held at each hedge venue.
GET /broker/v1/hedge/liveWhether this process sends real orders to a venue, and the state of its budget.
POST /broker/v1/hedge/live/armArm live hedging, for a limited time.
POST /broker/v1/hedge/live/killStop sending anything to a venue, now.

errors

Binance's codes, with Binance's meanings, so an existing client's error branch needs no change. Every failure is { "code": …, "msg": … }.

codemeaning
-1003Too many requests. Sent with Retry-After in seconds.
-1013Filter failure — tick size, lot size or minimum notional. See exchangeInfo.
-1021timestamp outside recvWindow, ahead of our clock, or a signature replayed after its window passed.
-1022The signature did not verify. Distinct from -2015 on purpose: a wrong signature and a key that may not do this are different problems.
-1102A mandatory parameter was missing, empty or malformed.
-1111Too many decimal places for this asset.
-1116 / -1117Invalid type / invalid side.
-1121Invalid symbol.
-1122We are not quoting that symbol right now. A state, not a fault — our reference price is stale and we will not deal blind.
-2010New order rejected.
-2011 / -2013Unknown order on cancel / order does not exist on query.
-2015Invalid key, address not allowed, or the key lacks the permission.
-2019Margin is insufficient — you cannot afford this.
-2021The order would trigger immediately.
-2022reduceOnly rejected.
-2027Exceeded the maximum position at this leverage. Not a collateral problem: depositing more will not help, so reduce size.
-4003 / -4005Quantity below zero / above the maximum.
-4028That leverage is not valid for this symbol.
-4046 / -4047Margin type already set / cannot be changed while a position or order is open.
-4131Requote. The price moved past what we quoted; send it again.
-4141Symbol closed — halted or delisted. Not a scheduled session break.

where we differ from Binance

  • We are the counterparty. There is no order book of other clients and no public trade tape, so there is no /fapi/v1/depth or /fapi/v1/trades. /broker/v1/quote/{symbol} is our price.
  • Anything of ours that is not in Binance's API lives under /broker/v1/. We add no fields to a /fapi/ response, so a strict client will not trip over them.
  • No /fapi/v1/time yet.
  • No websocket user stream yet. Poll /fapi/v2/positionRisk and /fapi/v1/userTrades.