Skip to main content
These read endpoints are how a bot answers the three questions it asks constantly: who am I, what do I hold, and how am I doing. They cover identity (/me), the money side of the wallet (/account/*), and a single cross-vertical roll-up of positions and trades across every group (/positions, /trades). Everything here is a read. The only “write” in the funding story is depositing USDC to an address you read from GET /account — and there is no withdrawal endpoint at all (see below).
Every monetary value on the wire is a full-precision plain decimal string in USD ("12.50", never a number, never raw base units, never scientific notation). Parse with a decimal library; round only for display. See Money & precision.
There is no withdrawal endpoint. Your API key can deploy funds (group positions, predictions, perps) but cannot move money out of the wallet to an external address — withdrawals require an interactive in-app session and have no REST route. Funding in is just a USDC transfer to the deposit address from GET /account. See Authentication.

Environment

Every request carries Authorization: Bearer $MURMO_API_KEY. The examples below use curl, fetch, and requests; swap in your HTTP client of choice.

Identity — GET /me

The bootstrap call. It echoes the user behind the credential, which key you’re using (metadata only — never the secret), and your rate budget. Nothing financial lives here; for balances and the deposit address use GET /account.
Response
For JWT (in-app) auth, apiKey and rateLimit are null and authMethod is "jwt". The Me fields:
The rate budget here matches Rate limits: 1,200 requests per 60-second window per key. Over it you get 429. Prefer WebSockets for live prices instead of tight REST polling.

Account summary and the deposit address

GET /account is the one call that gives you both your wallet’s headline value and the address to fund it.
Response
AccountSummary fields:
Field-naming nuance — read this once and save yourself a surprise. GET /account uses the Usd suffix (totalValueUsd, cashBalanceUsd). The other account/portfolio reads keep the upstream names without the suffix: /account/portfolio returns totalValue, absoluteChange24h, percentChange24h, and every per-balance row uses value and price (not valueUsd/priceUsd). They are all still decimal strings in USD — only the key names differ per endpoint.

Balances — GET /account/balances

Cash and token balances held in the main wallet. Each row is a WalletBalance; the response wraps them in a named balances array.
Response
WalletBalance fields:
balance is a decimal-string quantity and decimals is a structural integer — they play different roles. decimals is informational here (the balance is already humanized); you only need it to interpret the ...Raw base-unit quantities that show up elsewhere (trades). See Money & precision → Raw vs. human quantities.

Spot holdings — GET /account/positions

Token holdings (spot positions) in the main wallet — the same WalletBalance shape as /balances, plus a nextCursor for pagination when there are more pages.
Response
nextCursor is null when there are no more pages. When it is a string, pass it back to fetch the next page (it is null in the common single-page case).

Portfolio value and 24h change — GET /account/portfolio

Aggregate portfolio value plus 24-hour change, with the underlying positions inline.
Response
Portfolio fields:

PnL — GET /account/pnl

Just the 24h change numbers, when you don’t need the full portfolio body.
Response

Fund and check your account

1

Read the deposit address

GET /api/v1/account and read data.deposit.walletAddress (plus tokenMint to confirm you’re sending the right token — USDC).
2

Send USDC on Solana

Transfer USDC to that address on Solana. This is an on-chain transfer you make from your own wallet/exchange — it is not an API call. There is no API path that pulls funds for you.
3

Poll the balance until it lands

Re-GET /api/v1/account (or /account/balances) until cashBalanceUsd reflects the deposit. Settlement follows Solana confirmation, so allow a few seconds.
4

Deploy it

Once funded, the same key can open group proposals, predict, or open perps. To move money out, use the app — there is no withdrawal endpoint.

Cross-vertical positions and trades

Three endpoints under /api/v1 give you everything you hold and everything you’ve traded across all groups in one call, without iterating group by group. Spot, prediction, and perp positions each have their own per-vertical endpoints elsewhere; these are the aggregated views.
The data shapes differ — this is the most common cross-endpoint trip hazard. /positions and /positions/past return data as an object keyed by vertical ({ perps, spot, predictions }), not a bare array. /trades returns data as { spot, predictions }. The account list reads instead wrap rows in a named key ({ balances: [...] }, { positions: [...], nextCursor }). See the envelope table below before you write your parser.

Open positions — GET /positions

Open positions across perps + spot + predictions, in every group.
Response
The per-vertical item shapes are documented in full in the dedicated guides and the API Reference:
  • perps[]PerpPosition (the id is the assignmentId; reduce/claim it via the perps endpoints). Persisted/realized USD fields and live mark/PnL/price fields are all decimal strings; base-lot quantities (currentSizeBaseLots) are quantity strings.
  • spot[]SpotProposalWithPosition ({ proposal, userPosition, participantCount, participantAvatars }). Note userPosition.currentTokenAmountRaw is a base-unit quantity (humanize with the token’s decimals).
  • predictions[]PredictionWithPosition ({ proposal, market, userPosition, remainingTokenAmount, createdBy }). market is venue-native, normalized (marketTicker = market slug, eventTicker = event slug, prices already human dollar strings); tokenAmount/remainingTokenAmount are already humanized.

Past positions — GET /positions/past

Identical { perps, spot, predictions } shape, but for closed/resolved positions across all three verticals.

Trade history — GET /trades

Executed trade history for spot + predictions. (Perp fills are not here — surface those via GET /api/v1/perps/positions.)
Response
limit is optional (default 50, max 200; out-of-range values are clamped). Both arrays use the same money convention: tokenAmount is an already-humanized decimal-string quantity (interpret alongside tokenDecimals), and every ...Usd / pnl* field is a USD/percent decimal string (pnl* is null on opening buys). On prediction trades, totalCostUsd is the gross fill value (tokenAmount × pricePerTokenUsd), venueFeeUsd is the venue fee charged on the fill, and pnlPct/pnlUsd are net of venue fees.

data envelope cheat-sheet

The thing to internalize: every response is wrapped in a top-level { "data": ... }, but the shape inside data varies. Here is every endpoint on this page:
Two gotchas worth pinning: (1) the cross-vertical reads (/positions, /positions/past, /trades) are keyed objects, not arraysdata.spot, data.perps, data.predictions. (2) The account list reads bury their array under a named keydata.balances, data.positions. If you blindly data.map(...) either family, you’ll get a runtime error.

Errors

All four standard error statuses apply. 401 means a missing/invalid Authorization header or key; 404 on GET /account means the wallet couldn’t be resolved for the credential. Error bodies come in two shapes — coded business errors { message, code } and generic framework errors { statusCode, message, error }. See Errors.

Where to next

Money & precision

Why every value is a decimal string, and how to parse it.

Authentication

The Bearer header, what a key can (and can’t) do, and rate limits.

Groups & proposals

Why positions live as proposals inside groups — the model behind the spot/predictions arrays.

API Reference

Full schemas for Me, AccountSummary, WalletBalance, Portfolio, and CrossVerticalPositions.