Every monetary value in and out is a full-precision plain decimal string in USD (
"10.00", not
10 and not 10000000). That covers request fields like amountUsd and response fields like
totalCostUsd. See Money & precision.How a market is identified
You reference a market with three things, and you never send a title. The backend resolveseventTitle / marketTitle and the outcome token for you:
Proposals also carry an
outcomeMint: the venue’s outcome-token id for the chosen side (a long
numeric string), resolved server-side. It is an opaque identifier, not something you ever send.
Legacy proposals may hold older identifier formats there and in eventId / marketId.
Setup
Authorization: Bearer $MURMO_API_KEY. See Authentication.
Entering a prediction (creating a proposal, predicting on one) is restricted in some regions
and returns
403 with code GEO_RESTRICTED there. Selling, closing, and claiming are never
geo-restricted: you can always exit or redeem a position you hold. See
Eligibility & geo restrictions.Discovering events
Event reads are venue-native passthroughs, normalized to a stable shape: slugs in the...Ticker fields, prices as human dollar strings ("0.29"), times as epoch seconds. Each endpoint
returns { "data": ... } around the payload shown below.
Browse
GET /api/v1/predictions/events/browse returns the trending feed by default. Optional query params:
seriesTickers: a category tag slug such aspolitics,sports,crypto. Comma-separated values are accepted but only the first one is applied.limit: default20, max50.cursor: numeric offset for the next page.status: legacy filter, ignored.category(with optionaltag,page): when present, switches to the paginated category browse.categoryis a category label or slug (Politicsorpolitics),tagnarrows it, andpageis a zero-based page index. The response becomes{ "events": [...], "hasMore": true }.
Response (one event, trimmed to one market)
eventTicker/marketTickerare the slugs you send back aseventId/marketId.seriesTickeris the event’s primary category tag slug (also whatseriesTickersfilters on).yesSubTitleis the market’s row label in a grouped event (“JD Vance”); it is plainYeson a standalone YES/NO market.- Prices (
yesBidDollars,yesAskDollars,noBidDollars,noAskDollars,lastPriceDollars) are dollar strings between"0.0"and"1.0"; the NO side is the complement of YES. Buys fill near the ask of your side, sells near the bid. - Market
statusisactive,inactive,closed, orfinalized(resolved, redeemable);resultis""until resolution, thenyes,no, orinvalid. takerFeeBps/builderFeeBpsdescribe the venue fee applied at match on this market;0means the market currently charges none. The fee actually charged on each of your fills is reported on the trade asvenueFeeUsd.yesTokenId/noTokenIdare the venue outcome-token ids (the chosen one becomes the proposal’soutcomeMint).openTime,closeTime,expirationTime,startDateare epoch seconds;volumeandtotalVolumeare plain numbers.negRiskandconditionIdare venue-technical metadata; you can ignore them.- Some fields exist for shape compatibility and are always empty,
null, or fixed on the current venue:subTitle,competition,competitionScope,strikeDate,strikePeriod,strikeType,floorStrike,capStrike,openInterest,milestoneId,isTrending,isClosing,liveStatus,liveDetails,backgroundColor,imageScale, andcanCloseEarly(alwaysfalse).
Search
GET /api/v1/predictions/events/search fuzzy-searches events. q is required (a missing or
blank q is a 400). limit defaults to 20, max 50. sort, order, and cursor are
accepted but ignored: results are a single page and the response cursor is always 0.
Response (shape)
Live
GET /api/v1/predictions/events/live returns high-activity events, ordered by 24h volume. Optional:
limit (default 20, max 50), cursor (numeric offset), category and subcategory (both
category tag slugs; subcategory wins when both are sent). competition is accepted but ignored.
Response (shape)
nextCursor is null on the last page.
Event by slug
GET /api/v1/predictions/events/{slug} returns one event by its slug, with its markets filtered for
liquidity. An unknown slug is a 404 with code EVENT_NOT_FOUND. Older identifier formats from
legacy proposals also resolve here.
Related events
GET /api/v1/predictions/events/{slug}/related returns other events sharing this event’s primary category
tag, up to 10. seriesTicker must be sent (a missing or blank value is a 400), but its value
is unused on the current venue; any non-empty string works.
Creating a proposal (with the opening prediction)
POST /api/v1/predictions/proposals opens a proposal and places the creator’s first prediction in one
call. Group leaders only (non-leaders get a 403), and geo-restricted (see above).
The body is CreatePredictionProposalBody: groupId, eventId, marketId,
isYes, amountUsd, and optional reason (max 500 characters). All five non-reason fields are
required.
Buys must clear two minimums:
- $4 flat minimum (
400, codeAMOUNT_BELOW_MINIMUM). - The market’s venue minimum, typically 5 contracts at the current price (
400, codeMIN_ORDER_SIZE). The error message includes the market’s computed dollar minimum, so you can retry with a valid amount.
ACTIVE or still-settling (PENDING) proposal for the
same outcome exists in the group, creating another is a 400.
The response is { proposal, trade, pending, actionJobId } and fills are async-optimistic:
pending: false: the buy filled synchronously.tradeis populated andproposal.statusisACTIVE.pending: true: your funds are committed and the buy is settling in the background.tradeisnull,proposal.statusisPENDING, andactionJobIdidentifies the settlement job. The proposal flips toACTIVEon the confirmed fill (typically under a minute) or toFAILEDwith your funds automatically returned. PollGET /proposals/{id}to observe the flip.
Response (synchronous fill)
Response (asynchronous fill)
tokenAmount is the count of outcome contracts you hold, already humanized using
tokenDecimals (it is not named ...Raw). Every ...Usd field (pricePerTokenUsd,
totalCostUsd, venueFeeUsd) is a human decimal string. On an opening BUY, pnlPct / pnlUsd
are null.Predicting more
POST /api/v1/predictions/proposals/{id}/predict adds to your position on an existing proposal. Body is
just { "amountUsd": "..." } (required, a human USD string). Any member of the group can predict.
Geo-restricted like create, and the same two buy minimums apply (AMOUNT_BELOW_MINIMUM,
MIN_ORDER_SIZE).
The response is the same async-optimistic envelope, minus the proposal key:
{ trade, pending, actionJobId }. When pending is true, the buy settles in the background:
your position on GET /proposals/{id} grows once the fill confirms, and if the buy cannot complete
the funds are returned to your USDC balance. Predictions are only accepted while the proposal is
ACTIVE; anything else is a 409 with code PROPOSAL_INACTIVE. A proposal from the retired
legacy venue answers 409 PROPOSAL_SELL_ONLY — existing positions there can still sell and
claim, but no new predictions can enter.
Response (synchronous fill)
Selling (cashing out)
POST /api/v1/predictions/proposals/{id}/sell sells part or all of your position. Never
geo-restricted. The body (AmountOrMaxBody) takes exactly one of:
Sells are sized against the venue’s per-order minimum (typically 5 contracts):
- A partial sell below the minimum is floored up to the minimum, so you may sell slightly more than requested.
- A partial sell that would leave behind an unsellable sub-minimum remainder is extended to a full exit instead of stranding contracts you could never sell.
- A position entirely below the minimum cannot be sold (
400, codePOSITION_BELOW_MIN). No money moves; the position settles automatically when the market resolves.
422 (NOTHING_TO_SELL); no live market price to size
the sell is a 422 (NO_MARKET_PRICE, safe to retry).
Response
On a SELL,
pnlPct and pnlUsd are populated (here "4.38" means +4.38%) and are net of
venue fees on both the entry and the exit; totalCostUsd is the gross fill value before the
fee. Percentages are decimal strings expressed as a percent, see
Money & precision.Closing a proposal
POST /api/v1/predictions/proposals/{id}/close is creator-only and never geo-restricted. It
sells the creator’s entire remaining position and moves the proposal into a sell-only (CLOSED)
state so members can exit but not add. Closing with nothing left to sell is a 422
(NOTHING_TO_SELL); closing a proposal that is not ACTIVE is a 409 (PROPOSAL_INACTIVE).
Response
Claiming winnings
When the market resolves, the proposal’sstatus becomes RESOLVED, result is set, and didWin
tells you the outcome. POST /api/v1/predictions/proposals/{id}/claim redeems what the resolution
owes you, with no minimum:
- Winning outcomes redeem at $1 per contract.
- On an INVALID result (a 50/50 resolution), both sides redeem at $0.50 per contract.
didWinisfalsefor both sides on INVALID, so do not treatdidWin: falsealone as “nothing to claim”; checkresulttoo.
422 with code NOT_CLAIMABLE.
Response
The claim response carries the trade plus a top-level
amountClaimedUsd, the USD redeemed, as
a full-precision decimal string. On a winning CLAIM, pricePerTokenUsd is "1"; on an INVALID
claim it is "0.5". Claims charge no venue fee (venueFeeUsd is "0").Proposal statuses
PENDING and FAILED proposals are private to their creator: other members get a 404 on the
detail endpoint, and the default (unfiltered) list feed hides FAILED entirely and shows PENDING
only to its creator.
The full lifecycle
1
Discover an event
Browse or search the event-read endpoints to find an
eventId (event slug) and a marketId
(market slug) you want, and decide your side (isYes). Prices in these normalized payloads are
already human dollar strings.2
Create the proposal (opening prediction)
POST /predictions/proposals with groupId, eventId, marketId, isYes, and amountUsd
(at least $4 and the market’s venue minimum). Leaders only. If pending is true, poll the
proposal until it flips to ACTIVE (filled) or FAILED (funds returned).3
Predict more or sell
Add with
POST .../{id}/predict (amountUsd, may also return pending: true). Trim or exit
with POST .../{id}/sell: amountUsd for an approximate target, or max: true to close your
whole position.4
Resolution
When the market resolves, the proposal’s
status flips to RESOLVED and result / didWin
are filled in. (A creator can also close a proposal early to put it in sell-only mode.)5
Claim
If you won (
didWin: true), or the result is INVALID (both sides redeem at $0.50),
POST .../{id}/claim redeems your contracts for USDC and returns the CLAIM trade plus
amountClaimedUsd. Repeat calls return the same recorded claim.Reading proposals and positions
These reads are members-only: every per-group prediction read asserts group membership, so a non-member gets403 NOT_GROUP_MEMBER. See Errors.
List a group’s proposals
GET /api/v1/predictions/proposals?groupId=... returns the group’s prediction proposals, each with
your position. Optional status filter, one of PENDING, ACTIVE, CLOSED, RESOLVED, FAILED
(an unknown value is a 400). data is an array of
PredictionWithPosition. Remember the visibility rules: PENDING and FAILED
rows only ever appear for their creator.
PredictionWithPosition item:
One proposal’s detail
GET /api/v1/predictions/proposals/{id} returns { proposal, position }. position is null
when you hold nothing on this proposal. A non-member gets 403; an unknown id gets 404, and so
does someone else’s PENDING or FAILED proposal.
Response
The list endpoint nests your stake under
userPosition, while the single-proposal detail
endpoint nests it under position (and it’s null when you hold nothing). Both are the same
PredictionPosition shape.Fees and PnL
Three rules cover every money field on trades and positions:totalCostUsdon a trade is the gross fill value (tokenAmount × pricePerTokenUsd).venueFeeUsdis the venue fee charged at match on that fill: on top oftotalCostUsdon buys, deducted from delivery on sells. It is"0"when the market charges no taker fee, and on all claims (redemption is always fee-free). The fee rate varies by market category.pnlPct/pnlUsdon trades, and every PnL field on positions, are net of venue fees.
Field reference
PredictionProposal
PredictionTrade
PredictionPosition
Your stake on a proposal. Every ...Usd value is a MoneyString; every percent is a
DecimalString. All PnL fields are net of venue fees.
Errors you’ll see
Business errors carry a stablecode; branch on the code, not the message. Codes marked
retryable are safe to resend identically (no funds moved). See Errors for the
error shapes and the full catalog.
Where to next
Prediction agent
Find an event, predict, and claim in one script.
Money & precision
Why every amount is a decimal string, and how to parse it.
Groups & proposals
The shared social model behind every proposal.
Errors
The two error shapes and stable codes.
API Reference
Every Predictions endpoint, parameter, and schema.