Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

REST API

There are two hosts:

HostWhat it serves
https://v4-api.dev.parcllabs.comValidator. Live state: markets, orderbooks, accounts by owner, oracle, staking, /v1/ws.
https://v4-rest-api.dev.parcllabs.comREST API. Auth (/auth/*), signing (/tx/sign-and-submit), bridge, account history, leaderboard, stats, explorer.

The path tells you which host to hit. Each endpoint below carries a label.

All responses are JSON. Prices use 8 decimal places, sizes use 6, collateral (USDC) uses 6.

Field-naming convention

Most REST endpoints (markets, accounts, orderbook, oracle, staking) return camelCase field names: marketId, oraclePrice, lastFundingTimestamp.

Two surfaces use snake_case instead: GET /v1/markets/{id}/trades and every WebSocket event. Snake_case appears wherever the response shape mirrors an on-chain event (taker_order_id, tx_hash, taker_account_id). A client that deserializes both REST and WebSocket should expect either convention depending on the endpoint.

Markets

GET /v1/markets

Returns all markets.

curl https://v4-api.dev.parcllabs.com/v1/markets
[
  {
    "marketId": 0,
    "name": "NYC",
    "assetClass": "RealEstate",
    "status": "Active",
    "oraclePrice": "58035000000",
    "oracleStatus": "Active",
    "oracleValidUntil": 1712433900,
    "lastOraclePrice": "58035000000",
    "lastOracleTimestamp": 1712345678000,
    "markPrice": "58177000000",
    "bestBid": "58100000000",
    "bestAsk": "58200000000",
    "openInterestLong": "37460000",
    "openInterestShort": "37460000",
    "maxOpenInterest": "1000000000",
    "fundingRate": "-15800",
    "lastFundingTimestamp": 1712345678,
    "fundingIntervalSecs": 3600,
    "interestRateMicroBps": 64,
    "feeTiersEndpoint": "/v1/fee-tiers",
    "initialMarginRatioBps": 200,
    "maintenanceMarginRatioBps": 100,
    "backstopAccountId": 5,
    "marginTiers": [
      {
        "notionalFloor": "0",
        "maxLeverage": 50,
        "maintenanceMarginRatioBps": 100,
        "maintenanceDeduction": "0"
      },
      {
        "notionalFloor": "250000000000",
        "maxLeverage": 20,
        "maintenanceMarginRatioBps": 250,
        "maintenanceDeduction": "3750000000"
      }
    ],
    "priceBandBps": 1000,
    "depthThresholdLamports": "25000000000",
    "tickSize": "1000000"
  }
]

Real-estate markets have initialMarginRatioBps: 200 (50x leverage cap) and maintenanceMarginRatioBps: 100. priceBandBps is the per-update oracle sanity cap. depthThresholdLamports is the per-side notional depth (USDC, 6 decimals) that the impact-mid calculation walks on each side of the book. See Mark price.

Fees. The market object carries no fee rates. Fees are volume-tiered at the exchange level and depend on your trailing 30-day volume. Read GET /v1/fee-tiers (feeTiersEndpoint points there).

Breaking change (2026-07-28). Earlier versions of this endpoint included makerFeeBps / takerFeeBps (and briefly feeSchedule / baseTierMakerFeeBps / baseTierTakerFeeBps). Those fields are REMOVED: absent from the JSON, not null. A strict client schema that requires any of these fields will fail to parse every /v1/markets and /v1/markets/{id} response, not just fee reads. Mark the fields optional or drop them from your schema before consuming this API. The removed per-market values were fallback defaults, never the fee charged. Resolve fees from /v1/fee-tiers.

backstopAccountId is the market's required liquidation route. Every market has one; clients must not infer a global fallback.

marginTiers is the market's required nonempty size-based margin schedule, ordered by notionalFloor (USDC, 6 decimals). A tier applies to positions whose notional is at or above its floor and below the next tier's floor. Within a tier, maxLeverage caps leverage and maintenance margin is notional × maintenanceMarginRatioBps / 1e4 − maintenanceDeduction. The deduction keeps the requirement continuous across tier boundaries (see Margin tiers). A flat market carries one tier at floor zero matching the top-level ratios.

GET /v1/markets/{id}

Returns a single market by ID, with the same shape as one element of GET /v1/markets.

GET /v1/markets/{id}/orderbook

{
  "marketId": 0,
  "bids": [
    { "price": "58100000000", "size": "500000", "orderCount": 1 },
    { "price": "58050000000", "size": "1200000", "orderCount": 3 }
  ],
  "asks": [
    { "price": "58200000000", "size": "300000", "orderCount": 1 },
    { "price": "58250000000", "size": "800000", "orderCount": 2 }
  ]
}

GET /v1/markets/{id}/funding

Returns the current funding state for a market.

{
  "marketId": 0,
  "fundingRate": "69183",
  "lastFundingTimestamp": 1779819896651,
  "fundingIntervalSecs": 3600,
  "cumulativeFunding": "2388785636613712714",
  "maxFundingRateBps": 625
}

fundingRate is the running estimate that will settle on the next hourly crank. See Funding for the rate-computation details. cumulativeFunding is the per-market index that each position settles against.

GET /v1/markets/{id}/trades

Returns recent trades. The response is an array. Each element uses snake_case fields (maker_order_id, taker_order_id, tx_hash) to match the WebSocket fill event shape. See Field-naming convention.

Accounts

GET /v1/accounts/{id}

{
  "accountId": 12,
  "owner": "0x35504e68...",
  "mode": "Cross",
  "collateral": "21929491932",
  "positions": [
    {
      "marketId": 0,
      "side": "Long",
      "size": "64560188",
      "entryPrice": "58211606845",
      "unrealizedPnl": "-120297107",
      "cumulativeFunding": "0",
      "lastOraclePrice": "58090000000",
      "openedAt": 1712345678000,
      "tierGrandfathered": false
    }
  ],
  "openOrders": [],
  "createdAt": 1712300000000,
  "volume30d": 37581522814
}

tierGrandfathered is true when the position predates the market's current margin-tier table. A grandfathered position keeps the flat base margin rate and is reduce-only. You can hold or reduce it but not increase it.

GET /v1/accounts/by-owner/{pubkey}

Same response as above. Looks up by the owner's public key. Accepts both 0x-prefixed and bare hex (e.g. 0x35504e68... or 35504e68...).

GET /v1/accounts/{id}/positions

Returns just the positions array.

GET /v1/accounts/{id}/orders

Returns just the open orders array.

Oracle

GET /v1/oracle/{market_id}

Current oracle state for a market.

{
  "marketId": 0,
  "price": "58035000000",
  "lastUpdateDate": 20260406,
  "lastUpdateTimestamp": 1712345678000,
  "validUntil": 1712433900,
  "status": "Active"
}

GET /v1/oracle/{market_id}/history

Returns the historical oracle prices for a market.

{
  "marketId": 0,
  "prices": [
    { "time": 1748217600, "price": "59738000000" },
    { "time": 1748304000, "price": "59855999999" }
  ]
}

time is Unix seconds. price is the oracle value at that point, scaled to PRICE_EXPO = -8. Real-estate markets emit one point per day.

Exchange

GET /v1/fee-tiers

The effective trading-fee schedule. This is the only place the API states fee rates (/v1/markets carries none).

curl https://v4-api.dev.parcllabs.com/v1/fee-tiers
{
  "tier_count": 8,
  "tiers": [
    {
      "min_volume_30d": 0,
      "min_volume_30d_usd": "$0",
      "maker_fee_bps": 1,
      "taker_fee_bps": 10,
      "asset_class": "RealEstate"
    },
    {
      "min_volume_30d": 100000000000,
      "min_volume_30d_usd": "$100000",
      "maker_fee_bps": 0,
      "taker_fee_bps": 8,
      "asset_class": "RealEstate"
    }
  ]
}

Snake_case, matching the on-chain shape. min_volume_30d is the trailing 30-day notional volume in USDC (6 decimals). min_volume_30d_usd is the same value formatted. Negative maker_fee_bps is a rebate.

Matching rule: filter to tiers whose asset_class equals the market's assetClass or is null. A null class is a cross-class tier that applies to any market. Then take the highest tier whose min_volume_30d is at or below your trailing 30-day volume. Two tiers with the same min_volume_30d have no specified precedence. The live schedule has no such overlap.

GET /v1/node/info

{
  "protocol_version": 18,
  "binary_version": 18,
  "build_tag": "dev",
  "build_sha": "a6233d3",
  "build_version": "a6233d3",
  "block_height": 9494637,
  "block_timestamp": 1785362334775,
  "state_root": "0x38d85ae9..."
}

state_root is 0x-prefixed hex (32 bytes). build_sha identifies the exact binary the node runs. This endpoint returns snake_case fields, like the trades endpoint.

Transactions

POST /tx/sign-and-submit

Host: v4-rest-api.dev.parcllabs.com.

Submit a transaction body and have the server sign (via enclave) and forward it to the validator. Requires an API key (X-API-Key header). API keys have a trade-only scope. See authentication for the allowlist.

Request body:

{
  "transaction": {
    "PlaceOrder": {
      "account_id": 12,
      "market_id": 0,
      "side": "Long",
      "order_type": "Market",
      "price": 0,
      "size": 100000,
      "trigger_price": null,
      "reduce_only": false,
      "post_only": false,
      "time_in_force": "IOC"
    }
  },
  "nonce": 1713456789000,
  "timestamp": 1713456789
}

Response on success:

{
  "status": "success",
  "hash": "0x9dd57899...",
  "events": [
    { "type": "Fill", "market_id": 0, "price": "58100000000", "size": "100000", ... }
  ]
}

Response on failure:

{
  "status": "failed",
  "hash": "0x9dd57899...",
  "events": [],
  "error": "insufficient margin"
}

events is always present (empty on failure). It contains all state changes that the transaction caused: fills, order placements, cancellations, liquidations, etc.

Staking

GET /v1/staking/validators

Returns all registered validators with stake, commission, uptime, and status.

GET /v1/staking/validators/{pubkey}

Returns a single validator by public key.

GET /v1/staking/info

Returns network staking stats: total staked, active validators, epoch info, APY.

GET /v1/staking/balance/{pubkey}

Returns the PRCL staking balance for a public key.

GET /v1/staking/delegations/{pubkey}

Returns all delegations by a public key.

GET /v1/staking/unstakes/{pubkey}

Returns pending unstakes for a public key.

Leaderboard

GET /v1/leaderboard

Public. Returns the competition leaderboard sorted by PnL, along with competition metadata.

{
  "competition": {
    "name": "Devnet Season 1",
    "startTime": 1713398400000,
    "endTime": 1714608000000,
    "prizePool": "1000000000"
  },
  "leaderboard": [
    {
      "rank": 1,
      "accountId": 12,
      "owner": "0x35504e68...",
      "pnl": 4821000000,
      "roi": 0.4821,
      "accountValue": 14821000000,
      "volume": 98532000000,
      "tradeCount": 142
    }
  ]
}

pnl, accountValue, and volume are in USDC with 6 decimal places. roi is a decimal fraction (0.48 = 48%).

When there is no configured competition, competition is null.

Statistics

GET /v1/stats

Host: v4-rest-api.dev.parcllabs.com. Public. Returns aggregated exchange statistics.

{
  "totalVolume": "1234567890000",
  "volume24h": "98765432100",
  "totalTrades": 4821,
  "trades24h": 312,
  "totalAccounts": 87,
  "totalLiquidations": 3,
  "liquidations24h": 0,
  "totalBankruptcies": 1,
  "bankruptcies24h": 0,
  "totalLiquidationFees": "50000000",
  "totalDeposits": "8700000000000",
  "totalWithdrawals": "120000000000"
}

All monetary values are in USDC with 6 decimal places.

Account history

Host: v4-rest-api.dev.parcllabs.com.

These endpoints require authentication (JWT in the Authorization: Bearer header, or X-API-Key for bots). They return data only for accounts you own.

GET /v1/accounts/{id}/trades

Returns fill history for your account.

Query params:

  • limit: max results, up to 500 (default 100)
  • offset: pagination offset
  • startTime: Unix timestamp ms (inclusive)
  • endTime: Unix timestamp ms (exclusive)
  • marketId: filter to one market
[
  {
    "timestamp": 1713456789000,
    "marketId": 0,
    "side": "Long",
    "size": "100000",
    "price": "58150000000",
    "fee": "0",
    "closedPnl": null,
    "role": "taker",
    "orderId": 42,
    "txHash": "9dd57899..."
  }
]

closedPnl is the realized PnL this fill booked to your collateral (USDC, 6 decimals, as a string). It is null when the fill opened or increased a position.

GET /v1/accounts/{id}/trades/csv

Same as above but returns a CSV file. Useful for tax/accounting exports. Accepts the same query params except limit. The endpoint returns all matching records.

GET /v1/accounts/{id}/orders

Returns order history with fill status.

Query params:

  • limit: max results, up to 200 (default 50)
  • offset, startTime, endTime, marketId: same as trades
  • status: filter by status, one of open, filled, canceled, partially_filled, all (default all)
[
  {
    "orderId": 42,
    "timestamp": 1713456789000,
    "marketId": 0,
    "side": "Long",
    "price": "58000000000",
    "size": "100000",
    "filledSize": "100000",
    "avgFillPrice": "58150000000",
    "status": "filled",
    "cancelReason": null
  }
]

GET /v1/accounts/{id}/funding

Returns funding payment history.

Query params: limit, offset, startTime, endTime, marketId

[
  {
    "timestamp": 1713456000000,
    "marketId": 0,
    "side": "Long",
    "positionSize": "500000",
    "fundingRate": "-8750000",
    "payment": "-12500",
    "cumulativeFunding": "-12500"
  }
]

payment is in USDC lamports (6 decimals). Negative means you paid. Positive means you received.