REST API Endpoints#
Fee parameters in the response examples use shipped initialization defaults, not an observed live deployment. Read the response's actual values when quoting.
<!-- src: program/src/logic/fee.rs:70 DEFAULT_FEE_CAP_BPS --> <!-- src: program/src/logic/fee.rs:96 DEFAULT_DECAY_RATE_BPS -->This page documents the public read endpoints. For the complete REST reference — authentication, authenticated endpoints (orders, stats, achievements), and error semantics — see Full REST Reference.
Unless marked as an operational root endpoint, prepend /api/v1 to each path
(e.g. GET /api/v1/markets). Legacy root aliases for /v2/...,
/tx/..., /orders?tx=..., /config, /trades, /orderbook/top,
/status, and /admin/... remain served for compatibility but return
Deprecation: true plus canonical-link headers. New integrations should use
the /api/v1 forms shown here.
Complete Endpoint Inventory#
This inventory is checked against openapi.yaml by make docs-check. It is
intentionally compact; detailed request and response examples live in the
sections below and in Full REST Reference.
| Method | Path | Auth | operationId |
|---|---|---|---|
GET | /exchange/symbols | Public | getExchangeSymbols |
GET | /exchange/symbols/{symbol} | Public | getExchangeSymbol |
GET | /exchange/markets | Public | getExchangeMarkets |
GET | /exchange/markets/{symbol} | Public | getExchangeMarket |
GET | /exchange/orderbook/{symbol} | Public | getExchangeOrderbook |
GET | /exchange/quality | Public | getExchangeQuality |
GET | /exchange/quality/{symbol} | Public | getExchangeQualityBySymbol |
GET | /exchange/reconcile/{symbol} | Public | getExchangeReconcile |
GET | /exchange/account | Wallet | getExchangeAccount |
GET | /exchange/trades/{symbol} | Public | getExchangeTrades |
GET | /exchange/candles/{symbol} | Public | getExchangeCandles |
GET | /exchange/ticker/{symbol} | Public | getExchangeTicker |
GET | /external-markets/discover | Public | discoverExternalMarkets |
GET | /external-markets | Public | listExternalMarkets |
GET | /external-markets/{marketAddress} | Public | getExternalMarket |
GET | /external-markets/lookup | Public | lookupExternalMarketByProvider |
POST | /external-market-metadata | Wallet | ingestExternalMarketMetadata |
GET | /v2/reclaim/verifier-context | Public | getReclaimVerifierContext |
GET | /v2/reclaim/witness-context | Public | getReclaimWitnessContext |
GET | /v2/reclaim/create-build-context/{claimId} | Public | getReclaimCreateBuildContext |
GET | /v2/external-markets/{marketAddress}/maker-credit/{address} | Public | getExternalMarketMakerCredit |
GET | /external-events/{eventRefHash} | Public | getExternalEvent |
GET | /external-markets/{marketAddress}/fees | Public | getExternalMarketFees |
GET | /markets | Public | getMarkets |
GET | /markets/current | Public | getMarketsCurrent |
GET | /markets/{marketId} | Public | getMarketsByMarketId |
GET | /markets/{marketId}/creator-fee-claims | Public | getMarketsByMarketIdCreatorFeeClaims |
GET | /markets/{marketId}/protocol-fees | Public | getMarketsByMarketIdProtocolFees |
GET | /markets/{marketId}/trades | Public | getMarketsByMarketIdTrades |
GET | /markets/{marketId}/candles | Public | getMarketsByMarketIdCandles |
GET | /positions | Wallet | getPositions |
GET | /responsible-trading | Wallet | getResponsibleTrading |
PUT | /responsible-trading | Wallet | putResponsibleTrading |
GET | /orders | Wallet | getOrders |
DELETE | /orders/{orderId} | Wallet | deleteOrdersByOrderId |
POST | /orders/{orderId}/cancel | Wallet | postOrdersByOrderIdCancel |
GET | /user/profile | Wallet | getUserProfile |
PUT | /user/profile | Wallet | putUserProfile |
GET | /user/streak | Wallet | getUserStreak |
GET | /user/balance | Wallet | getUserBalance |
GET | /user/orders | Wallet | getUserOrders |
GET | /user/positions | Wallet | getUserPositions |
GET | /stats | Wallet | getStats |
GET | /stats/platform | Public | getStatsPlatform |
GET | /stats/history | Wallet | getStatsHistory |
GET | /stats/{wallet}/pnl-history | Wallet | getStatsByWalletPnlHistory |
GET | /stats/trades | Wallet | getStatsTrades |
GET | /alerts | Wallet | getAlerts |
POST | /alerts | Wallet | postAlerts |
DELETE | /alerts/{alertId} | Wallet | deleteAlertsByAlertId |
GET | /watchlist/markets | Public | getWatchlistMarkets |
GET | /watchlist | Wallet | getWatchlist |
POST | /watchlist | Wallet | postWatchlist |
DELETE | /watchlist/{marketId} | Wallet | deleteWatchlistByMarketId |
GET | /leaderboard | Public | getLeaderboard |
GET | /leaderboard/{period} | Public | getLeaderboardByPeriod |
GET | /achievements | Wallet | getAchievements |
GET | /achievements/all | Public | getAchievementsAll |
GET | /activity/{wallet} | Wallet | getWalletActivity |
GET | /challenges/today | Public | getChallengesToday |
GET | /challenges/progress | Wallet | getChallengesProgress |
GET | /challenges/history | Wallet | getChallengesHistory |
POST | /notifications/register | Wallet | postNotificationsRegister |
POST | /notifications/unregister | Wallet | postNotificationsUnregister |
GET | /notifications/tokens | Wallet | getNotificationsTokens |
GET | /notifications/prefs | Wallet | getNotificationsPrefs |
PUT | /notifications/prefs | Wallet | putNotificationsPrefs |
GET | /notifications/inbox | Wallet | getNotificationsInbox |
PUT | /notifications/inbox/{notificationId}/read | Wallet | putNotificationsInboxRead |
GET | /config | Public | getConfig |
GET | /v2/fee-config | Public | getV2FeeConfig |
GET | /v2/referral/{address} | Public | getV2ReferralByAddress |
GET | /v2/referrer/{address}/earnings | Public | getV2ReferrerByAddressEarnings |
GET | /v2/referrals/diagnostics | Wallet | getV2ReferralsDiagnostics |
GET | /v2/referrals/leaderboard | Public | getV2ReferralsLeaderboard |
GET | /v2/wallet/{address}/analytics | Public | getV2WalletByAddressAnalytics |
POST | /v2/handles | Wallet | postV2Handles |
GET | /v2/handles/{query} | Public | getV2HandlesByQuery |
POST | /v2/share | Wallet | postV2Share |
GET | /v2/share/{id} | Public | getV2ShareById |
POST | /tx/create-market-pull | Wallet | postTxCreateMarketPull |
GET | /pyth/pull/land/providers | Wallet | getPythPullLandProviders |
POST | /pyth/pull/land | Wallet | postPythPullLand |
GET | /pyth/pull/land/{attemptId} | Wallet | getPythPullLandStatus |
POST | /tx/place-order | Wallet | postTxPlaceOrder |
POST | /tx/cancel-order | Wallet | postTxCancelOrder |
POST | /tx/redeem | Wallet | postTxRedeem |
POST | /tx/claim-creator-fees | Wallet | postTxClaimCreatorFees |
POST | /tx/set-referrer | Wallet | postTxSetReferrer |
POST | /tx/claim-referrer-earnings | Wallet | postTxClaimReferrerEarnings |
GET | /creators | Public | getCreators |
GET | /creators/{address} | Public | getCreatorsByAddress |
POST | /compliance/verify-integrity | Wallet | postComplianceVerifyIntegrity |
POST | /compliance/confirm-jurisdiction | Wallet | postComplianceConfirmJurisdiction |
GET | /compliance/jurisdiction-attestation | Wallet | getComplianceJurisdictionAttestation |
POST | /attestation/challenge | Wallet | postAttestationChallenge |
POST | /attestation/verify | Wallet | postAttestationVerify |
GET | /status | Public | getStatus |
GET | /time | Public | getTime |
GET | /trades | Public | getTrades |
GET | /orderbook/top | Public | getOrderbookTop |
GET | /admin/events | Wallet | getAdminEvents |
GET | /admin/fee-totals | Wallet | getAdminFeeTotals |
GET | /admin/protocol-fees | Wallet | getAdminProtocolFees |
GET | /admin/alerts | Wallet | getAdminAlerts |
GET | /admin/referrer-treasury/{shard} | Wallet | getAdminReferrerTreasuryByShard |
Exchange Facade#
The exchange facade is a read-only, terminal-friendly view of Seesaw markets. It gives bots and third-party terminals canonical symbols, current market metadata, book snapshots, trades, candles, and ticker values without adding a new custody, signing, webhook, or order-entry trust boundary.
Canonical symbols use this format:
SEESAW:<BASE>-<QUOTE>:<YYYYMMDDTHHMM>:<DURATION>:<YES|NO>
Example:
SEESAW:SOL-USD:20260708T1200:15M:YES
Use these routes when an integration wants exchange-style identifiers and normalized read models:
| Path | Description |
|---|---|
/exchange/symbols | Lists canonical YES and NO instruments for indexed markets |
/exchange/symbols/{symbol} | Resolves one canonical symbol |
/exchange/markets | Lists exchange-style market summaries |
/exchange/markets/{symbol} | Returns one exchange-style market summary |
/exchange/orderbook/{symbol} | Returns current depth with snapshot sequence and checksum |
/exchange/quality | Returns global exchange facade freshness and quality |
/exchange/quality/{symbol} | Returns symbol-level freshness and quality |
/exchange/reconcile/{symbol} | Returns market, book, latest trade, and recovery metadata |
/exchange/account | Returns signed-wallet positions, collateral, and risk summary |
/exchange/trades/{symbol} | Returns the recent trade tape for the symbol's market |
/exchange/candles/{symbol} | Returns maintained candle buckets for the symbol's market |
/exchange/ticker/{symbol} | Returns best bid, best ask, last price, and 24h volume |
TypeScript integrations can use the strict REST helpers exported from
@seesaw/core/api (fetchExchangeSymbols, fetchExchangeSymbol,
fetchExchangeMarkets, fetchExchangeMarket, fetchExchangeOrderbook,
fetchExchangeTicker, fetchExchangeQuality, fetchExchangeSymbolQuality,
fetchExchangeReconcile, and fetchExchangeAccount). These helpers unwrap the
standard API envelope, validate response shape at runtime, and URL-encode
canonical symbols for path parameters. fetchExchangeAccount requires wallet
authentication and returns HTTP 401 otherwise; callers must pre-configure the
ApiClient with its auth option or call client.setAuthToken(...) first.
For live or replayable activity streams, use a user-owned Solana RPC or stream provider with the Trustless Stream Consumer Recipes. The hosted exchange facade is a convenience read surface, not the source of custody or signing truth.
/exchange/account includes a risk object for terminal dashboards:
current position value, max loss, max payout, authoritative claimable amount
when available, concentration warning, asset exposures, and per-market
loss/payout breakdowns. Historical settled payouts remain in
balances[].settledPayouts and are not counted as live account value. Amounts
are settlement-mint base-unit decimal strings.
Markets#
List Markets#
GET /markets
Query Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
state | integer | - | Filter by state (0=Pending, 1=Created…5=Closed) |
pair | string | - | Filter by mapped Pyth pair, e.g. SOL/USD; unknown pairs return no matches |
duration | number | - | Filter by duration_seconds |
resolved | number | - | Use resolved=1 for terminal resolved/closed markets with an outcome |
limit | number | 50 | Items per page (max 100) |
offset | number | 0 | Pagination offset (max 1,000,000) |
chain | string | solana | solana; non-Solana values are not supported for open beta |
Response (wrapped in data envelope):
{
"data": {
"markets": [
{
"id": 1,
"address": "ABcd...1234",
"marketId": "1937600",
"pythFeed": "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace",
"state": 2,
"outcome": null,
"tStart": "2026-02-12T00:00:00.000Z",
"tEnd": "2026-02-12T00:15:00.000Z",
"startPrice": "68523400000",
"startPriceExpo": -8,
"totalVolume": "15000000",
"totalTrades": 42,
"totalPositions": 18
}
],
"total": 96,
"limit": 50,
"offset": 0,
"bookAsOfSlot": "298451237"
}
}
bestBid/bestAsk on each row are a top-of-book snapshot aggregated from the
same open orders the detail route pages, so the list carries the same freshness
marker the detail response does — see
Book freshness below.
Get Current Market#
Returns the active trading market (state=2) or the next upcoming market.
GET /markets/current
Response (wrapped in data envelope):
{
"data": {
"market": {
"id": 1,
"address": "ABcd...1234",
"marketId": "1937600",
"pythFeed": "0xff61...",
"settlementMint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
"tStart": "2026-02-12T00:00:00.000Z",
"tEnd": "2026-02-12T00:15:00.000Z",
"startPrice": "68543210000",
"startPriceConf": "12345000",
"startPriceExpo": -8,
"endPrice": null,
"outcome": null,
"totalYesShares": "150000000000",
"totalNoShares": "120000000000",
"totalVolume": "500000000000",
"state": 2
},
"status": "trading"
}
}
state is an integer (0=Pending, 1=Created, 2=Trading, 3=Settling, 4=Resolved,
5=Closed). status is "trading" or "upcoming". Returns 404 if no current or
upcoming market exists.
Get Market by ID#
GET /markets/:marketId
Parameters:
| Name | Type | Description |
|---|---|---|
marketId | string | Market address (base58) or numeric market ID |
Response (wrapped in data envelope):
{
"data": {
"market": {
"...": "all market list fields",
"accumulatedCreatorFees": "12000",
"tickSizeBps": 100,
"feeConfig": {
"feeCapBps": 200,
"decayRateBps": 400
}
},
"orderbook": {
"bids": [{ "price": 6500, "quantity": "10000000", "orders": 3 }],
"asks": [{ "price": 7000, "quantity": "8000000", "orders": 2 }]
},
"bookAsOfSlot": "298451237"
}
}
Book freshness#
bookAsOfSlot (string | null) is served by both GET /markets and
GET /markets/:marketId. It is the slot the served book snapshot was read
at — stamped at the moment the snapshot is taken and carried with it through
the server's short-lived level cache, so a cache hit reports the age of the
levels it served rather than how far the subscriber has advanced since.
Because the stamp is taken immediately before the read, it is a lower bound: the snapshot reflects at least everything committed through that slot, and never claims freshness it does not have.
null means freshness is unknown (no slot has been processed yet, or the
subscriber is disabled) — never slot zero, and never "fresh". Clients compare it
against lastProcessedSlot from GET /status to decide
whether to warn that a quoted book may have moved. A staleness warning is an
affordance only: it must never block an action the program would accept.
tickSizeBps carries the live order-book tick size used for canonical YES-price
rounding previews. feeConfig carries the live fee-curve parameters:
feeCapBps (ceiling) and decayRateBps (slope). These are the inputs consumed
by the slippage estimator in @seesaw/core — see
SDK fee guide for client-side estimation.
Tick size defaults to 100 when no config row has been indexed. Market detail
fails closed until an authoritative program-scoped fee-config event is available.
The market also exposes per-market creator-fee claims at:
GET /markets/:marketId/creator-fee-claims
Returns an array of claim events: { marketAddress, creatorTokenAccount, caller, amount, slot, signature, claimedAt }.
Fee Config#
Get Current Fee Config#
Returns the current and historical fee-curve parameters plus the four-way
allocation and maker resting-age gate. The deprecated root alias GET /v2/fee-config remains available for
older clients.
GET /api/v1/v2/fee-config
Response (unwrapped):
{
"current": {
"changed_at": "2026-02-01T00:00:00.000Z",
"fee_cap_bps": 200,
"decay_rate_bps": 400,
"protocol_share_bps": 5000,
"creator_share_bps": 500,
"referral_share_bps": 500,
"maker_rebate_share_bps": 4000,
"maker_rebate_min_rest_seconds": 10,
"fee_model_version": 1,
"tx_signature": "5wHu..."
},
"history": [
{
"changed_at": "2026-01-01T00:00:00.000Z",
"fee_cap_bps": 300,
"decay_rate_bps": 700,
"protocol_share_bps": 5000,
"creator_share_bps": 500,
"referral_share_bps": 500,
"maker_rebate_share_bps": 4000,
"maker_rebate_min_rest_seconds": 10,
"fee_model_version": 1,
"tx_signature": "3xKp..."
}
],
"invariants": {
"feeSplitValid": true,
"invalidFeeSplitRows": 0
}
}
The shipped allocation is 50% protocol / 5% creator / 5% referral / 40% maker rebate. Eligible maker fills credit the maker's free quote balance; ineligible maker allocation and rounding dust go to protocol, as does the referral allocation without an eligible referrer. See fee constants and allocation details.
<!-- src: program/src/logic/fee.rs:168 FeeSplit4::TARGET --> <!-- F-01: update with builder fee allocation -->The response above is illustrative: current uses shipped defaults; the older
history row shows a custom curve. These dates and signatures are placeholders.
History returns up to 25 rows in reverse-chronological order. Fields use
snake_case. The client SDK (@seesaw/core) maps these to feeCapBps /
decayRateBps in the camelCase FeeConfig type.
The fee formula is:
fee_bps(price) = min(fee_cap_bps, decay_rate_bps × (10000 − price) / 10000)
At the shipped cap of 200 bps and decay of 400 bps, the fee is capped through price 5000 bps and falls toward zero above it. The rate uses integer floor division; the fee amount rounds up to settlement-token base units. Read deployed config for current values; see fee constants.
<!-- src: program/src/logic/fee.rs:96 DEFAULT_DECAY_RATE_BPS -->Order Book#
Get Top of Book#
Returns the best bid and ask for a market. The deprecated root alias
GET /orderbook/top remains available for older clients.
GET /api/v1/orderbook/top
Query Parameters:
| Name | Type | Description |
|---|---|---|
market | string | Market address (optional; uses most recent if omitted) |
Response (unwrapped, no data envelope):
{
"best_bid": 5800,
"best_ask": 6000,
"market": "ABcd...1234"
}
market is absent from the response when no markets exist. best_bid and
best_ask are null if there are no resting orders on that side.
Orders#
List Orders#
Returns authenticated wallet orders with optional status and market filters.
GET /orders
Query Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
status | string | No | One of open, filled, cancelled, expired, pending_cancel, cancel_failed. |
market | string | No | Market address filter. |
cursor | string | No | Opaque keyset cursor returned as nextCursor from the previous page. Cannot be combined with offset. |
limit | number | No | Number of orders (default: 50, max: 100). The server fetches limit + 1. |
offset | number | No | Legacy offset pagination parameter. Cannot be combined with cursor. |
Response (data envelope):
{
"data": {
"orders": [
{
"id": "4f63464b-8d2f-4f2a-aa0c-c278c8a77698",
"orderId": "7",
"marketAddress": "ABcd...1234",
"marketId": "12345",
"settlementMint": "Mint...Address",
"side": "yes",
"sideCode": 0,
"sideLabel": "buy_yes",
"priceBps": 5800,
"quantity": "5000000",
"filledQuantity": "0",
"status": "open",
"createdAt": "2026-02-12T00:05:00.000Z",
"expiresAt": "1787746500",
"expiredClaimable": false
}
],
"total": 1,
"limit": 50,
"offset": 0,
"nextCursor": null
}
}
Order quantity fields are decimal strings. nextCursor is null when there
is no next page.
Reclaim eligibility: expiresAt and expiredClaimable#
expiresAt is the per-order TTL deadline as unix seconds in a decimal
string — not an ISO instant like createdAt. It is the value the program
compares against the Clock sysvar, so it can be passed straight into the
reclaimExpiredOrderVerdict preflight. null means no projected deadline is
known: either a decoded order carries the legacy stored-zero sentinel or the
indexer never observed the placing instruction. Live placement normalizes a
zero request to the market-close cap observed at placement. Never render
null as expired.
expiredClaimable answers whether ReclaimExpiredOrder would clear its
eligibility gate. On chain that gate is
status == ExpiredClaimable || is_age_expired(now): the first disjunct is a
flag the match loop writes into the market's orderbook account, the second is
the TTL above. The API reads both, and reports a tri-state:
| Value | Meaning |
|---|---|
true | The gate would pass — the slot is flagged ExpiredClaimable, or the TTL has elapsed on a still-open order. |
false | The gate would fail, and the indexer can see why — the order is no longer open, or a fresh orderbook snapshot shows the slot active and not yet due, or absent. |
null | The orderbook state is unavailable or too stale to speak: RPC failure, missing orderbook account, a snapshot older than the order, or lookup budget exhausted. |
The key is always present, and null is never collapsed to false. Clients
must treat null as "unknown": leave the reclaim affordance enabled and show
no countdown. Rendering null as false hides a reclaim the program would
have accepted.
One case still sits outside this field: the terminal-forfeit branch (a resolved
market past CLOSE_TIMEOUT_SECONDS) bypasses the gate entirely and is a
market-level fact, evaluated by clients from the market snapshot.
The same two facts appear on the public GET /orders?tx=<signature> lookup as
expires_at and expired_claimable, with identical semantics.
Trades#
Get Trades#
Returns recent trades or looks up trades by transaction signature. Root-level
path — also accessible at /api/v1/trades.
For a market-scoped tape, use GET /markets/{marketId}/trades; it injects
the same market filter and supports the same cursor and limit parameters.
GET /trades
GET /markets/{marketId}/trades
Query Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
tx | string | No | On-chain transaction signature. Cannot be combined with cursor. |
market | string | No | Market address filter for recent-trade pages. |
cursor | string | No | Opaque keyset cursor returned as nextCursor from the previous page. |
limit | number | No | Number of trades (default: 100, max: 500). The server fetches limit + 1. |
Response (unwrapped, no data envelope):
{
"trades": [
{
"id": 42,
"market_address": "ABcd...1234",
"maker_order_id": "7",
"taker_order_id": "8",
"maker": "Maker...Address",
"taker": "Taker...Address",
"price_bps": 5800,
"quantity": "5000000",
"maker_side": 1,
"taker_fee": "48720",
"fee_protocol": "24360",
"fee_creator": "2436",
"fee_referral": "2436",
"protocol_treasury_index": 0,
"tx_signature": "5wHu...",
"slot": 312456789,
"executed_at": "2026-02-12T00:05:00.000Z"
}
],
"nextCursor": "eyJzbG90IjoiMzEyNDU2Nzg5IiwiZmlsbF9pbmRleCI6MX0"
}
For this single-fill example at shipped defaults, notional is 2,900,000 base units and the rate is 168 bps: the fee is 48,720. An eligible maker receives 19,488 in free quote balance; creator and eligible referral each receive 2,436, leaving protocol 24,360. Those four allocations sum to the fee. This response schema does not expose the maker-rebate field, so its three displayed allocation fields alone do not sum to the total. With an ineligible maker, that allocation also goes to protocol.
<!-- src: program/src/logic/fee.rs:168 FeeSplit4::TARGET --> <!-- src: apps/indexer/src/api/routes/trades.ts:55 TradeResponseRow -->Trade fields use snake_case. protocol_treasury_index (also aliased as
treasury_index for back-compat) identifies which of the 8 protocol treasury
shards received the protocol fee. tx_signature is also aliased as signature.
Token amounts and fees are decimal strings. nextCursor is null when there
is no next page.
CSV export: send Accept: text/csv or format=csv to receive the same page as
comma-separated rows. CSV export honors market, cursor, and limit; clients
that need the next page should keep using the JSON response to read nextCursor.
Get Market Candles#
Returns OHLCV buckets for a market from the incrementally maintained
market_trade_candles table. Candle volume is the sum of filled share quantity
in the bucket and is returned as a decimal string.
GET /markets/{marketId}/candles?interval=300&limit=500
Query Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
interval | number | No | Candle interval in seconds: 60, 300, 900, 3600, or 86400. |
limit | number | No | Number of candles (default: 500, max: 1000). |
Response:
{
"data": {
"market": "ABcd...1234",
"interval": 300,
"candles": [
{
"bucketStart": "2026-02-12T00:05:00.000Z",
"openPriceBps": 5800,
"highPriceBps": 5900,
"lowPriceBps": 5700,
"closePriceBps": 5850,
"volume": "5000000",
"tradeCount": 3
}
]
}
}
Indexer Status#
Get Indexer Status#
Returns freshness and connectivity status for the indexer.
GET /status
Response (wrapped in data envelope):
{
"data": {
"status": "healthy",
"synced": true,
"slotLag": 2,
"lastProcessedSlot": "250000000",
"lastActivityAt": "2026-02-12T00:05:00.000Z",
"indexerRunning": true,
"pullLifecycle": {
"ready": true,
"stale": false,
"updatedAt": "2026-02-12T00:05:00.000Z",
"counts": {
"tradingPastEnd": 0,
"withoutEndPriceNearExpiry": 0,
"unresolvedPastExpiry": 0,
"deferredCreatorFees": 0,
"terminalMissingEndSnapshot": 0
}
},
"head": {
"lagSlots": 2,
"lagSeconds": 0.8
},
"rpc": {
"healthy": true,
"connected": true,
"wsConnected": true,
"connectedDurationSeconds": 300,
"disconnectedDurationSeconds": null
},
"oracleFreshness": {
"status": "green",
"updatedAt": "2026-02-12T00:05:00.000Z",
"feeds": [
{
"feedId": "ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d",
"status": "green",
"freshness": "fresh",
"ageSeconds": 4,
"lastUpdateAt": "2026-02-12T00:04:56.000Z",
"activeMarkets": 2,
"source": "market_microstructure"
}
]
},
"incidentFree24h": {
"value": null,
"status": "unknown",
"scope": "process-local",
"since": null,
"reason": "No durable incident log is available for this process"
},
"subsystems": {
"database": "green",
"rpc": "green",
"websocket": "green",
"indexer": "green",
"pullLifecycle": "green",
"oracleFreshness": "green"
}
}
}
status is one of "healthy", "degraded", or "unhealthy". synced is
true when the indexer has processed at least one slot, the database and RPC
are connected, slot lag is within the healthy read threshold, lifecycle pulls are
ready, and no failed transaction work is pending. Use synced to distinguish
"genuinely no data" from "not yet indexed".
Public status signals use "green", "amber", "red", or "unknown".
incidentFree24h.scope is currently "process-local" because the public status
route does not read a durable incident log.
Server Time#
Use the canonical versioned endpoint GET /api/v1/time to sample the API
server clock. serverTime is Unix time in milliseconds and iso is the
matching UTC ISO-8601 value captured from the same clock read. Responses include
Cache-Control: no-store; there is no root /time compatibility alias.
{
"data": {
"serverTime": 1786000000123,
"iso": "2026-08-06T07:06:40.123Z"
},
"meta": {
"requestId": "request-id",
"asOf": { "slot": "unknown" }
}
}
Positions#
List Authenticated Wallet Positions#
Returns positions for the authenticated wallet. Requires authentication headers (see Full REST Reference).
GET /positions
Query Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
settled | string | - | Filter: true or false |
limit | number | 50 | Items per page (max 100) |
offset | number | 0 | Pagination offset |
Response (wrapped in data envelope):
{
"data": {
"positions": [
{
"id": 7,
"address": "Pos...Address",
"marketAddress": "ABcd...1234",
"owner": "Wallet...Address",
"market": {
"pythFeed": "0xff61...",
"state": 4,
"outcome": 1
},
"yesShares": "1000000",
"noShares": "0",
"collateralDeposited": "650000",
"totalBought": "1000000",
"totalSold": "0",
"settled": false,
"payout": "0",
"currentYesPrice": 6500
}
],
"balance": "5000000",
"total": 3,
"limit": 50,
"offset": 0
}
}
Responsible Trading#
Read Server-Backed Controls#
Returns the authenticated wallet's account-wide responsible-trading controls. These controls are stored server-side so web and mobile clients see the same cool-off and daily deposit-limit state.
GET /responsible-trading
Response (wrapped in data envelope):
{
"data": {
"walletAddress": "Wallet...Address",
"coolOffUntil": "2026-07-05T16:00:00.000Z",
"blocked": true,
"dailyDepositLimit": "50000000",
"updatedAt": "2026-07-04T16:00:00.000Z"
}
}
Update Server-Backed Controls#
Extends the authenticated wallet's account-wide cool-off and/or sets a daily
deposit limit. Cool-off updates are monotonic: submitting a shorter duration
cannot shorten an active server-side cool-off. The hosted POST /tx/place-order
builder rejects authenticated quote-locking orders when today's indexed
quote-locking order collateral plus the requested order collateral would exceed
dailyDepositLimit.
PUT /responsible-trading
Request body:
{
"coolOffHours": 24,
"dailyDepositLimit": "50000000"
}
Authenticated User#
These endpoints use the wallet-authenticated request headers described in Full REST Reference. They always operate on the signed wallet; callers cannot override the subject wallet in the body.
Profile#
GET /user/profile
PUT /user/profile
GET /user/profile returns the signed wallet's display profile, including the
wallet address, optional handle/display fields, and timestamps. PUT /user/profile updates mutable display metadata only; it does not change
referral locks, settlement accounts, or any on-chain state.
Streak, Balance, Orders, And Positions#
GET /user/streak
GET /user/balance
GET /user/orders
GET /user/positions
GET /user/streak returns the current authenticated trading streak and daily
activity metadata. GET /user/balance returns indexed settlement-token balance
information for the signed wallet. GET /user/orders and
GET /user/positions are authenticated wallet aliases for order and position
summaries; prefer GET /orders and GET /positions when you need filters or
pagination.
Authenticated Stats#
Wallet Stats#
GET /stats
GET /stats/history
GET /stats/:wallet/pnl-history
GET /stats/trades
GET /stats returns the signed wallet's aggregate trading metrics. GET /stats/history returns the authenticated wallet's time-series stats. GET /stats/:wallet/pnl-history returns PnL history for the path wallet, but still
requires wallet authentication to prevent unauthenticated scraping. GET /stats/trades returns the authenticated wallet's trade history. Use
GET /stats/platform for public platform-wide aggregates.
Alerts#
Price Alerts#
GET /alerts
POST /alerts
DELETE /alerts/:alertId
Alerts are wallet-scoped. GET /alerts lists the signed wallet's configured
alerts. POST /alerts creates a new alert for a market/feed threshold and
returns the stored alert row. DELETE /alerts/:alertId deletes only an alert
owned by the signed wallet; missing or foreign alert ids return the same
not-found shape to avoid account enumeration.
Watchlist#
Wallet Watchlist#
GET /watchlist
POST /watchlist
DELETE /watchlist/:marketId
GET /watchlist returns the signed wallet's saved markets. POST /watchlist
adds a market to that wallet's list and is idempotent for an already-saved
market. DELETE /watchlist/:marketId removes the market for the signed wallet
only. Public discovery remains available at GET /watchlist/markets.
Achievements And Challenges#
Achievements#
GET /achievements
GET /achievements/all
GET /achievements returns the signed wallet's unlocked achievements and
progress. GET /achievements/all returns the public achievement catalog.
Daily Challenges#
GET /challenges/today
GET /challenges/progress
GET /challenges/history
GET /challenges/today returns the public challenge set. GET /challenges/progress and GET /challenges/history require wallet
authentication and return the signed wallet's current progress and historical
completion records.
Notifications#
Device Token Registration#
POST /notifications/register
POST /notifications/unregister
GET /notifications/tokens
Notification tokens are wallet-scoped. Registering associates a device token
with the signed wallet; unregistering removes that token for the same wallet.
GET /notifications/tokens is an authenticated debugging/readback endpoint for
the signed wallet's registered tokens and should not be exposed as a public
directory.
Handles And Share Cards#
Handles#
POST /api/v1/v2/handles claims or updates the signed wallet's vanity handle.
Handle ownership is bound to the authenticated wallet. GET /api/v1/v2/handles/:query resolves a public handle or wallet query for display.
Share Cards#
POST /api/v1/v2/share creates a wallet-authenticated share-card record from
server-side indexed stats; clients must not submit arbitrary PnL or ranking
figures. Identical live share payloads are deduplicated and return the existing
id instead of creating unbounded duplicate rows. GET /api/v1/v2/share/:id
returns the public immutable share payload by id until the 30-day share-link
lifetime expires; expired ids return 404.
Hosted Transaction Builders#
Hosted transaction builders return unsigned Solana transaction payloads for the signed wallet to review and sign. They are convenience builders only: the on-chain program remains authoritative for market timing, account ownership, fee conservation, referral precedence, confidence guards, and settlement invariants. Clients must not treat a successful builder response as execution.
POST /tx/create-market-pull
POST /tx/place-order
POST /tx/cancel-order
POST /tx/redeem
POST /tx/claim-creator-fees
POST /tx/set-referrer
POST /tx/claim-referrer-earnings
create-market-pull builds the supported Pull create flow. place-order
applies server-side responsible-trading checks before returning a quote-locking
order transaction. The remaining builders prepare cancellation, redemption,
creator-fee claim, referrer setup, and referrer-earnings claim transactions for
the authenticated wallet.
Compliance And Attestation#
Integrity And Jurisdiction#
POST /compliance/verify-integrity
POST /compliance/confirm-jurisdiction
GET /compliance/jurisdiction-attestation
POST /compliance/verify-integrity records client integrity evidence for the
signed wallet and current app context. POST /compliance/confirm-jurisdiction
records the wallet's current jurisdiction attestation. GET /compliance/jurisdiction-attestation returns the latest attestation state for
the signed wallet.
Wallet Attestation#
POST /attestation/challenge
POST /attestation/verify
POST /attestation/challenge creates a short-lived challenge for the wallet to
sign. POST /attestation/verify verifies the signed challenge and returns the
server's attestation result. These endpoints complement, but do not replace,
the normal wallet-authenticated API headers.
Admin Read Endpoints#
Admin endpoints require wallet authentication and server-side authorization. Unauthorized callers receive the same API error envelope as other protected routes; authenticated admin responses are read-only operational views.
GET /admin/events
GET /admin/fee-totals
GET /admin/alerts
GET /admin/referrer-treasury/:shard
GET /admin/events returns indexed administrative program events. GET /admin/fee-totals returns aggregate protocol, creator, and referral fee
accounting. GET /admin/alerts returns operational alerts from the indexer.
GET /admin/referrer-treasury/:shard returns accounting for one referrer
treasury shard.
Referral#
Get Referral for a Wallet (Referee Lookup)#
Returns the active referral lock for a wallet, if one exists. The deprecated
root alias GET /v2/referral/:wallet remains available for older clients.
GET /api/v1/v2/referral/:wallet
Parameters:
| Name | Type | Description |
|---|---|---|
wallet | string | Referee wallet address |
Response (unwrapped):
{
"success": true,
"data": {
"wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"referrer": "8yLYtg3CW87d97TXJSDpbD5jBkheTqA83TZRuJosgBsV",
"expiresAt": 4070908800000,
"isLocked": true,
"createdAt": "2026-01-01T00:00:00Z"
}
}
Returns data: null when no referral lock exists for the wallet. The
resolveReferrer function in @seesaw/core calls this endpoint as its
first-priority data source. See the
Referral and creator fees guide for the
full precedence rules.
Get Referrer Earnings#
Returns the accumulated and claimed earnings for a referrer wallet.
The deprecated root alias GET /v2/referrer/:wallet/earnings remains available
for older clients.
GET /api/v1/v2/referrer/:wallet/earnings
Parameters:
| Name | Type | Description |
|---|---|---|
wallet | string | Referrer wallet address |
Response (unwrapped):
{
"success": true,
"data": {
"wallet": "8yLYtg3CW87d97TXJSDpbD5jBkheTqA83TZRuJosgBsV",
"accumulated": "4500000",
"totalClaimed": "1000000",
"refereesCount": 12,
"lastCreditAt": 1704067500000,
"settlementMint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
}
}
accumulated is the claimable balance in settlement-mint base units. Claim via
ClaimReferrerEarnings (0x24) or seesaw fee claim-referral.
Referral Leaderboard#
Returns top referrers by accumulated earnings. The deprecated root alias
GET /v2/referrals/leaderboard remains available for older clients.
GET /api/v1/v2/referrals/leaderboard?limit=<n>
Default limit is 25, max is 100.
Referral Diagnostics#
Returns referral accounting health data. The deprecated root alias
GET /v2/referrals/diagnostics remains available for older clients.
GET /api/v1/v2/referrals/diagnostics
Wallet Analytics#
Returns a unified public earnings profile: trading PnL, creation, and referral.
The deprecated root alias GET /v2/wallet/:address/analytics remains available
for older clients.
GET /api/v1/v2/wallet/:address/analytics
Creators#
Get Creator Detail#
Returns markets created by a wallet with per-market accumulated creator fees.
Valid wallet addresses that have not created any markets return 200 with
zeroed totals and empty markets / deferredFees arrays, so dashboards can
use this endpoint as an empty-state data source without treating non-creators
as failed requests.
GET /creators/:wallet
Parameters:
| Name | Type | Description |
|---|---|---|
wallet | string | Creator wallet address |
Response (wrapped in data envelope):
{
"data": {
"walletAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"totalFeesEarned": "1500",
"marketsCreated": 3,
"totalVolume": "5000000",
"markets": [
{
"address": "ABcd...1234",
"pair": "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
"state": 3,
"volume": "3000000",
"trades": 42,
"feesEarned": "1000",
"createdAt": "2026-01-01T00:00:00Z"
}
],
"deferredFees": [
{
"marketAddress": "ABcd...1234",
"accumulatedCreatorFees": "1000",
"settlementMint": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
"outcome": 1,
"pair": null,
"pythFeedId": null
}
]
}
}
deferredFees lists markets where ClaimCreatorFees can be called now (market
is in Resolved or Closed state with non-zero accumulated fees). Pass this
response directly to listCreatorMarkets in @seesaw/core to get a typed
CreatorMarketEntry list. See the
Referral and creator fees guide for full
examples.
Note: The
pairfield contains the raw Pyth feed ID (32-byte hex string), not a human-readable symbol. Map to a display name using your own feed-ID lookup.
See Also#
- Full REST Reference — authentication and authenticated endpoints
- WebSockets — real-time updates
- SDK Guide — client integration
- SDK Guide — fee estimation and account reads
- Referral and creator fees guide