> ## Documentation Index
> Fetch the complete documentation index at: https://docs.limitless.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# Unrealized PnL Leaderboard by Market

> Live leaderboard of open positions for one market, ranked by Unrealized PnL or ROI. Public - no authentication required. Ranks are absolute across pages, so page 2 with `limit=10` returns ranks 11-20.

Results are paginated with `limit` (1-100, default 100) and `page` (1-100, default 1). The response also includes `totalRows` and `totalPages` describing the committed Top-100 generation for the market. `ETag` values are page-specific, so a validator returned for one page will not match another.

Responses are served from a live Redis projection maintained by a dedicated worker. When the projection is warm the response is a `READY` snapshot with a strong `ETag` and short client cache. When the projection is still being rebuilt, callers may receive a `BUILDING`, `STALE`, or `DEGRADED` snapshot with `Cache-Control: no-store` and (for `BUILDING`) an empty `data` array - retry after a short delay. If the market is not open (`RESOLVED`, hidden, or otherwise ineligible), the endpoint returns `404`.

Ranks every currently open position in one market by live Unrealized PnL or ROI, using the same mark price the frontend renders. Public — no authentication required.

<Info>
  A dedicated worker computes the rankings from a live projection. Successful responses include a strong `ETag`; pass it back as `If-None-Match` to get `304 Not Modified` while the projection is unchanged. `ETag` values are page-specific — validators do not cross pages.
</Info>

## Pagination

Results are paginated with `limit` (1–100, default `100`) and `page` (1–100, default `1`). Ranks stay absolute across pages, so page 2 with `limit=10` returns ranks 11–20. Each response includes:

* `page` — the page returned.
* `totalRows` — number of ranked rows in the committed Top-100 generation for the market.
* `totalPages` — total number of pages given the requested `limit`.

Request the first page of a "Top Holders" view with `limit=10&page=1`, then increment `page` until `page === totalPages`. To fetch the complete Top-100 in one call, use `limit=100&page=1`. Omitting `page` is treated as page 1, so existing clients keep working.

## State field

Every response carries a `state`. Treat it as the readiness signal:

| State      | Meaning                                   | How to handle                                                          |
| ---------- | ----------------------------------------- | ---------------------------------------------------------------------- |
| `READY`    | Complete, current snapshot.               | Render `data` and cache per `Cache-Control`.                           |
| `STALE`    | Last complete snapshot is being replaced. | Render `data`; response is `Cache-Control: no-store`, refetch shortly. |
| `DEGRADED` | Snapshot returned with a `staleReason`.   | Render `data`; treat as best-effort and refetch shortly.               |
| `BUILDING` | No complete snapshot yet.                 | `data` is empty; retry after a short delay.                            |

For live refresh, subscribe to the [`unrealizedPnlProjectionChanged`](/developers/websocket/unrealized-pnl#unrealizedpnlprojectionchanged) WebSocket event and refetch this endpoint when a hint arrives for the same `marketId`.

<Note>
  This endpoint returns `404` for markets that are not open — resolved, hidden, or otherwise ineligible for a live leaderboard.
</Note>


## OpenAPI

````yaml GET /leaderboard/pnl/unrealized/markets/{marketId}
openapi: 3.0.0
info:
  title: Limitless Exchange API
  description: >-

    # Limitless Exchange Trading API


    *Production-ready API for prediction market trading, portfolio management,
    and market data*


    > 🎯 **Quick Navigation**: [Authentication](#tag/authentication) |
    [Markets](#tag/markets) | [Trading](#tag/trading) |
    [Portfolio](#tag/portfolio)


    ---
      


    ## 🚀 Quick Start


    Choose your preferred programming language for complete end-to-end
    implementation:


    ### Overview


    The Limitless Exchange API offers both REST and WebSocket integration:


    **REST API (Trading)**:

    1. **🔐 Authentication**: Use API key for all programmatic access

    2. **📊 Fetch Market Data**: Get market info including venue contract
    addresses (once per market)

    3. **📋 Order Creation**: Build and sign orders using EIP-712 structured
    data

    4. **🚀 Order Submission**: Submit signed orders and receive confirmations


    **WebSocket API (Real-Time Data)**:

    1. **🔌 Connection**: Connect to `/markets` namespace for real-time updates

    2. **📊 Subscriptions**: Subscribe to market prices and position changes

    3. **📡 Events**: Handle live market data and transaction updates


    ### 🔐 Authentication for API Users


    > **⚠️ DEPRECATION NOTICE**: Cookie-based session authentication is
    deprecated and will be removed within weeks. Please migrate to API keys
    immediately.


    | Method | Header | Status |

    |--------|--------|--------|

    | **API Key** | `X-API-Key: lmts_...` | ✅ Required for programmatic access |

    | Cookie Session | `Cookie: limitless_session=...` | ⚠️ Deprecated (removal
    imminent) |


    **Getting an API Key**


    API keys can only be created via the Limitless Exchange UI:

    1. Log in to [limitless.exchange](https://limitless.exchange) using your
    wallet

    2. Click your profile menu (top right)

    3. Select "Api keys"

    4. Generate a new key


    **Using Your API Key**


    Include in all requests via the `X-API-Key` header:


    ```bash

    # REST API

    curl -H "X-API-Key: lmts_your_key_here"
    https://api.limitless.exchange/markets


    # WebSocket - pass X-API-Key header during connection handshake

    ```


    ### Migration from Cookie to API Key


    If you're currently using cookie-based authentication, migrate by:


    1. **Generate an API key** via the UI (profile menu → Api keys)

    2. **Replace cookie header** with API key header:


    ```diff

    # Before (deprecated)

    - Cookie: limitless_session=your_session_token


    # After

    + X-API-Key: lmts_your_key_here

    ```


    3. **Remove session management code** - no more login flow or cookie
    handling needed


    ### Important: Venue System for CLOB Markets


    CLOB markets use a **venue system** where each market is associated with
    specific contract addresses. Before placing orders:


    1. **Fetch market data once**: `GET /markets/:slug` returns venue
    information

    2. **Use venue.exchange**: This is the `verifyingContract` for EIP-712 order
    signing

    3. **Cache the venue**: Venue data is static per market - fetch once and
    reuse


    **Sample venue response:**

    ```json

    {
      "venue": {
        "exchange": "0xA1b2C3...",
        "adapter": "0xD4e5F6..."
      }
    }

    ```


    ### Required Approvals


    Before trading, set up token approvals based on order type:


    | Order Type | Market Type | Approve To |

    |------------|-------------|------------|

    | BUY | All CLOB | USDC → `venue.exchange` |

    | SELL | Simple CLOB | CT → `venue.exchange` |

    | SELL | NegRisk/Grouped | CT → `venue.exchange` AND `venue.adapter` |


    ### Checksummed Addresses


    All addresses must use **checksummed format** (EIP-55 mixed-case):

    - Authentication: `x-account` header

    - Orders: `maker` and `signer` fields

    - Example: `0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed`


    ### Implementation Guides


    **[🐍 Python Quick Start](#description/-python-quick-start)**

    - REST API: eth-account, requests, and web3.py libraries

    - WebSocket: python-socketio, asyncio integration


    **[☕ Java Quick Start](#description/-java-quick-start)**  

    - REST API: Web3j, OkHttp3, and Jackson libraries


    **[📦 Node.js/TypeScript Quick
    Start](#description/-nodejs-typescript-quick-start)**

    - REST API: viem, ethers, and cross-fetch libraries

    - WebSocket: socket.io-client for real-time trading

    - Full TypeScript support with end-to-end examples


    **[🔌 WebSocket Integration](#description/-websocket-integration)**

    - Real-time market data and position updates

    - Production-ready Python client with authentication


    ---
      


    ## 🐍 Python Quick Start


    Complete end-to-end Python implementation for Limitless Exchange API
    integration.
      


    ### 🐍 Python E2E Order Creation Guide


    Complete Python implementation guide is being loaded from external
    documentation...


    **Guide Contents:**

    - 🔐 Complete authentication flow with eth-account

    - 📋 Order construction with Web3.py calculations

    - ✍️ EIP-712 structured data signing

    - 🚀 Order submission with requests library

    - ⚠️ Comprehensive error handling

    - 🛠️ Production deployment considerations


    *For the complete Python guide, ensure the file is available at
    docs/scripts-samples/python-e2e-order-creation.md*
        


    ## ☕ Java Quick Start


    Complete end-to-end Java implementation for Limitless Exchange API
    integration.
      


    ### ☕ Java E2E Order Creation Guide


    Complete Java enterprise implementation guide is being loaded from external
    documentation...


    **Guide Contents:**

    - 🔐 Complete authentication flow with Web3j

    - 📋 Order construction with BigInteger precision

    - ✍️ EIP-712 structured data signing

    - 🚀 Order submission with OkHttp3

    - ⚠️ Enterprise error handling patterns

    - 🏗️ Production Maven project structure


    *For the complete Java guide, ensure the file is available at
    docs/scripts-samples/java-e2e-order-creation.md*
        


    ## 📦 Node.js/TypeScript Quick Start


    Complete end-to-end Node.js/TypeScript implementation for trading and
    WebSocket subscriptions.
      


    ### 📦 Node.js/TypeScript Trading & WebSocket Guide


    Complete Node.js/TypeScript implementation guide is being loaded from
    external documentation...


    **Guide Contents:**

    - 🔐 **Authentication**: Wallet-based auth with ethers and viem

    - 📋 **Order Creation**: EIP-712 signing with viem WalletClient

    - 🚀 **Order Submission**: REST API integration with cross-fetch

    - 🔌 **WebSocket Subscriptions**: socket.io-client for real-time updates

    - 📊 **Market Data**: AMM prices and CLOB orderbook subscriptions

    - ⚠️ **Type Safety**: Full TypeScript support with proper types

    - 🛠️ **Production Ready**: Complete end-to-end working example


    **Key Features:**

    - **Combined Subscriptions**: Subscribe to both AMM and CLOB markets
    simultaneously

    - **Authentication Flow**: Complete wallet-based authentication with session
    management

    - **Trading Integration**: Place orders and receive real-time updates

    - **TypeScript First**: Type-safe implementation with proper interfaces


    *For the complete Node.js guide, ensure the file is available at
    docs/scripts-samples/node-socket-trading-and-subscribe.md*
        


    ## 🔌 WebSocket Integration


    Real-time market data and position updates using WebSocket connections.
      


    ### 🔌 WebSocket Real-Time Integration Guide


    Complete WebSocket implementation guide is being loaded from external
    documentation...


    **Guide Contents:**

    - 🔌 **WebSocket Connection**: python-socketio client with async support

    - 🔐 **Authentication**: JWT session cookie integration

    - 📊 **Market Subscriptions**: Real-time price updates and position changes

    - ⚡ **Event Handling**: Comprehensive event processing patterns

    - 🔄 **Auto-Reconnection**: Production-ready reconnection logic

    - 🛠️ **Error Recovery**: Robust error handling and fallback strategies


    **Key Features:**

    - **Public Mode**: Market price updates without authentication

    - **Authenticated Mode**: Full access to positions and transactions

    - **Multi-Market Support**: Subscribe to multiple markets simultaneously

    - **Production Ready**: Tested patterns for production deployment


    *For the complete WebSocket guide, ensure the file is available at
    docs/scripts-samples/python-socket-subscribe.md*
        
  version: '1.0'
  contact:
    name: API Support
    url: https://limitless.exchange
    email: hey@limitless.network
servers:
  - url: https://api.limitless.exchange
    description: Production API
security: []
tags:
  - name: Authentication
    description: User authentication and session management
  - name: Markets
    description: Browse, search, and analyze prediction markets
  - name: Market Navigation
    description: Navigation tree, market pages, and property filters
  - name: Trading
    description: Create, manage, and cancel orders
  - name: Portfolio
    description: Position tracking, trade history, and performance
  - name: Feed
    description: Public trading activity feeds
paths:
  /leaderboard/pnl/unrealized/markets/{marketId}:
    get:
      tags:
        - Leaderboard
      summary: Get Unrealized PnL leaderboard for a market
      description: >-
        Live leaderboard of open positions for one market, ranked by Unrealized
        PnL or ROI. Public - no authentication required. Ranks are absolute
        across pages, so page 2 with `limit=10` returns ranks 11-20.


        Results are paginated with `limit` (1-100, default 100) and `page`
        (1-100, default 1). The response also includes `totalRows` and
        `totalPages` describing the committed Top-100 generation for the market.
        `ETag` values are page-specific, so a validator returned for one page
        will not match another.


        Responses are served from a live Redis projection maintained by a
        dedicated worker. When the projection is warm the response is a `READY`
        snapshot with a strong `ETag` and short client cache. When the
        projection is still being rebuilt, callers may receive a `BUILDING`,
        `STALE`, or `DEGRADED` snapshot with `Cache-Control: no-store` and (for
        `BUILDING`) an empty `data` array - retry after a short delay. If the
        market is not open (`RESOLVED`, hidden, or otherwise ineligible), the
        endpoint returns `404`.
      operationId: UnrealizedPnlLeaderboardController_getMarket
      parameters:
        - name: marketId
          in: path
          required: true
          description: Numeric market id.
          schema:
            type: integer
            minimum: 1
        - name: metric
          in: query
          required: false
          description: Metric to rank by.
          schema:
            type: string
            enum:
              - pnl
              - roi
            default: pnl
        - name: limit
          in: query
          required: false
          description: Number of leaderboard entries to return per page (1-100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: page
          in: query
          required: false
          description: >-
            Page number to return (1-100). Ranks stay absolute across pages, so
            page 2 with `limit=10` returns ranks 11-20. Omitting `page` returns
            page 1.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 1
      responses:
        '200':
          description: Ranked Unrealized PnL leaderboard for the market
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnrealizedPnlMarketResponseDto'
        '304':
          description: Snapshot matches the caller's `If-None-Match` ETag
        '400':
          description: Invalid market id or query parameters
        '404':
          description: Market is not open for Unrealized PnL
components:
  schemas:
    UnrealizedPnlMarketResponseDto:
      type: object
      properties:
        schemaVersion:
          type: integer
          enum:
            - 1
        state:
          $ref: '#/components/schemas/UnrealizedPnlSnapshotState'
        projectionVersion:
          type: string
          nullable: true
        scopeVersion:
          type: string
          nullable: true
        presentationVersion:
          type: string
          nullable: true
        asOf:
          type: string
          format: date-time
          nullable: true
        markAsOf:
          type: string
          format: date-time
          nullable: true
        staleReason:
          type: string
          nullable: true
        collateralToken:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/UnrealizedPnlCollateralTokenDto'
        scope:
          type: string
          enum:
            - MARKET
        marketId:
          type: integer
          minimum: 1
        metric:
          type: string
          enum:
            - pnl
            - roi
        limit:
          type: integer
          minimum: 1
          maximum: 100
        page:
          type: integer
          minimum: 1
          default: 1
        totalRows:
          type: integer
          minimum: 0
        totalPages:
          type: integer
          minimum: 0
        data:
          type: array
          items:
            $ref: '#/components/schemas/UnrealizedPnlMarketEntryDto'
      required:
        - schemaVersion
        - state
        - scope
        - marketId
        - metric
        - limit
        - page
        - totalRows
        - totalPages
        - data
    UnrealizedPnlSnapshotState:
      type: string
      description: >-
        Readiness of the underlying projection. `READY` is a complete, current
        snapshot. `BUILDING` means the projection is being rebuilt and `data` is
        empty. `STALE` and `DEGRADED` return the last complete snapshot with
        `Cache-Control: no-store`; callers should retry shortly.
      enum:
        - BUILDING
        - DEGRADED
        - READY
        - STALE
    UnrealizedPnlCollateralTokenDto:
      type: object
      properties:
        id:
          type: integer
          example: 7
        symbol:
          type: string
          example: USDC
        address:
          type: string
          example: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
        decimals:
          type: integer
          example: 6
        priceOracleId:
          type: string
          nullable: true
          example: usd-coin
      required:
        - id
        - symbol
        - address
        - decimals
        - priceOracleId
    UnrealizedPnlMarketEntryDto:
      type: object
      properties:
        rank:
          type: integer
          minimum: 1
        account:
          type: string
        username:
          type: string
          nullable: true
        displayName:
          type: string
          nullable: true
        pfpUrl:
          type: string
          nullable: true
        side:
          type: string
          enum:
            - 'YES'
            - 'NO'
            - UP
            - DOWN
        outcomeLabel:
          type: string
          nullable: true
        heldShares:
          $ref: '#/components/schemas/UnrealizedPnlAmountValueDto'
        avgEntry:
          $ref: '#/components/schemas/UnrealizedPnlAmountValueDto'
        costBasis:
          $ref: '#/components/schemas/UnrealizedPnlMoneyValueDto'
        mark:
          $ref: '#/components/schemas/UnrealizedPnlMarkValueDto'
        marketValue:
          $ref: '#/components/schemas/UnrealizedPnlMoneyValueDto'
        unrealizedPnl:
          $ref: '#/components/schemas/UnrealizedPnlMoneyValueDto'
        unrealizedRoi:
          type: number
          nullable: true
          description: ROI as a percent (e.g. 37.5 = +37.5%). Null when cost basis is zero.
      required:
        - rank
        - account
        - username
        - displayName
        - pfpUrl
        - side
        - outcomeLabel
        - heldShares
        - avgEntry
        - costBasis
        - mark
        - marketValue
        - unrealizedPnl
        - unrealizedRoi
    UnrealizedPnlAmountValueDto:
      type: object
      description: >-
        Numeric amount rendered both as an integer `raw` string in collateral
        base units and a human-readable `formatted` decimal string.
      properties:
        raw:
          type: string
          example: '100000000'
        formatted:
          type: string
          example: '100'
      required:
        - raw
        - formatted
    UnrealizedPnlMoneyValueDto:
      type: object
      description: >-
        Money amount in the market's collateral with an optional USD-denominated
        value.
      properties:
        raw:
          type: string
          example: '9000000'
        formatted:
          type: string
          example: '9'
        usd:
          type: string
          nullable: true
          example: '9'
      required:
        - raw
        - formatted
        - usd
    UnrealizedPnlMarkValueDto:
      type: object
      description: >-
        Current mark price used to value the open position, plus the source that
        produced it and the time it was observed.
      properties:
        raw:
          type: string
        formatted:
          type: string
        source:
          type: string
          enum:
            - CLOB_DEPTH_WEIGHTED_BID
            - CLOB_LAST_MINED_TRADE
            - AMM_SPOT
            - AMM_LAST_INDEXED_TRADE
            - AVG_ENTRY_FALLBACK
        asOf:
          type: string
          format: date-time
          nullable: true
      required:
        - raw
        - formatted
        - source
        - asOf

````