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

# Hyperliquid open positions and unrealized PnL

> GET /v1/analytics/user-state returns an address's open Hyperliquid perp positions, entry and mark prices and unrealized PnL, in the clearinghouseState shape.

## When to use

Use `user-state` when you need a **point-in-time picture** of where an
address sits on Hyperliquid — currently open markets, position sizes,
entry prices, mark prices, and unrealized P\&L. This is the canonical
"what are they holding right now?" endpoint.

For history (how those positions evolved, individual fills, funding
payments), use [`/v1/fills`](/api-reference/market-data/list-fills),
[`/v1/analytics/user-funding`](/api-reference/analytics/user-funding),
or [`/v1/analytics/user-maker-taker`](/api-reference/analytics/user-maker-taker).

## Conventions

* `size` is **signed**: positive = long, negative = short. Markets with
  `|size| < 1e-9` are filtered (effectively closed).
* `unrealized_pnl = size * (mark_price - entry_price)`.
* `entry_price` is the VWAP entry across all fills that built the current
  position; matches HL's `clearinghouseState.assetPositions[].entryPx`.
* `as_of_ts` (unix ms) carries the wall-clock of the latest `mark_prices`
  row used. If this is more than a few seconds old, the mark-price
  poller is lagging; the position size + entry are still accurate but
  the unrealized P\&L will be stale.

## Example

```bash theme={null}
curl -H "Authorization: Bearer $IRONFLOW_API_KEY" \
  "https://api.ironflow.sh/v1/analytics/user-state?address=0x0c684f333a7e120bce61383da670bbb0157e82d0"
```


## OpenAPI

````yaml GET /v1/analytics/user-state
openapi: 3.1.0
info:
  title: Ironflow API
  version: 1.0.0
  description: >
    Hyperliquid market data from Ironflow's own nodes: tick-level fills and

    trades, candles, funding rates, liquidations, mark prices, open interest,

    markets and wallet analytics over REST. One schema across native HL perps,

    HIP-3 builder markets, HIP-4 outcome contracts and HL spot.


    **Base URL:** `https://api.ironflow.sh`


    **Authentication:** Read endpoints work without a key (keyless access) or

    with a Bearer token in the `Authorization` header. Writes always need a key.

    ```

    Authorization: Bearer if_your_api_key

    ```

    Get a free key at https://ironflow.sh/key.


    **Access levels:**


    | Access | Requests per minute | History | Max range per request |

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

    | No key | 10 per IP | 24 hours | 1 hour |

    | Free key | 60 | 30 days | 6 hours |

    | Explorer | 100 | 90 days | 24 hours |

    | Builder | 1,000 | 1 year | 7 days |

    | Enterprise | 10,000 | full | unlimited |


    Without a market filter, high-volume endpoints allow at most 4 hours per
    request.

    A header that is present but malformed returns 401; keyless access applies
    only

    when no `Authorization` header is sent.


    **Data availability:** fills and trades cover a rolling 12 months; other

    datasets start later (funding 2025-09-27, open interest and mark prices

    2026-04-29).


    **Timestamps:** All timestamps are Unix milliseconds (int64).


    **Pagination:** Paginated endpoints return a `cursor` in
    `pagination.next_cursor`.

    Pass it as the `cursor` query parameter to fetch the next page.

    When `pagination.has_more` is `false`, you have reached the last page.
  contact:
    name: Ironflow Support
    url: https://ironflow.sh
servers:
  - url: https://api.ironflow.sh
    description: Production
security:
  - bearerAuth: []
  - {}
tags:
  - name: Market Data
    description: Historical tick-level market data stored in ClickHouse.
  - name: Analytics
    description: Aggregated analytics endpoints (Builder tier and above).
  - name: Cohorts
    description: Predefined and custom address groups for filtering market data.
  - name: Info Proxy
    description: >-
      Cached passthrough to the Hyperliquid Info API. No record caps, higher
      rate limits.
  - name: Export
    description: Bulk data export as CSV or Parquet (Builder tier and above).
  - name: Query
    description: >-
      Read-only ClickHouse SQL over curated Hyperliquid tables (hl.*), limited
      by tier.
  - name: System
    description: Health checks and metadata endpoints.
  - name: TradingApp
    description: >-
      Trading app Phase A — wallet auth (SIWE), agent-key provisioning, and
      kill-switch.
paths:
  /v1/analytics/user-state:
    get:
      tags:
        - Analytics
      summary: Current open positions + unrealized P&L
      description: |
        Snapshot of an address's current open perp positions, entry prices,
        mark prices, position values, and unrealized P&L. Mirrors the shape
        of Hyperliquid's `clearinghouseState` `/info` request so callers
        porting from HL's API can reuse their parsing.

        Sign convention: `size` is signed (positive = long, negative = short).
        `unrealized_pnl = size * (mark_price - entry_price)`. Markets with
        `|size| < 1e-9` are omitted (effectively flat).

        Returns `as_of_ts` (unix ms) carrying the wall-clock of the latest
        `mark_prices` row used; clients can detect staleness if the poller
        is lagging.
      operationId: getUserState
      parameters:
        - $ref: '#/components/parameters/address'
      responses:
        '200':
          description: Current state for the address.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserState'
        '400':
          $ref: '#/components/responses/QueryWindowExceeded'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '504':
          $ref: '#/components/responses/QueryTimeout'
components:
  parameters:
    address:
      name: address
      in: query
      description: Wallet address filter (hex string, e.g. `0xabc123`).
      schema:
        type: string
        example: '0xabc123def456'
  schemas:
    UserState:
      type: object
      description: Snapshot of an address's current open positions + uPnL.
      required:
        - address
        - as_of_ts
        - asset_positions
        - margin_summary
      properties:
        address:
          type: string
          example: '0x0c684f333a7e120bce61383da670bbb0157e82d0'
        as_of_ts:
          type: integer
          format: int64
          description: Unix ms of the latest mark_price used
          example: 1779394567000
        asset_positions:
          type: array
          items:
            $ref: '#/components/schemas/AssetPosition'
        margin_summary:
          $ref: '#/components/schemas/UserMarginSummary'
    AssetPosition:
      type: object
      description: One open market position. `size` is signed (positive = long).
      required:
        - market
        - market_class
        - size
        - entry_price
        - mark_price
        - position_value
        - unrealized_pnl
        - realized_pnl
      properties:
        market:
          type: string
          example: BTC-PERP
        market_class:
          type: string
          enum:
            - perp
            - spot
            - prediction
            - option
          example: perp
        size:
          type: number
          format: double
          example: 0.1234
          description: Signed; positive = long, negative = short
        entry_price:
          type: number
          format: double
          example: 78000
        mark_price:
          type: number
          format: double
          example: 78500
        position_value:
          type: number
          format: double
          example: 9690
        unrealized_pnl:
          type: number
          format: double
          example: 61.7
        realized_pnl:
          type: number
          format: double
          description: Lifetime cumulative
          example: 150
        return_on_equity:
          type: number
          format: double
          example: 0.00637
        leverage:
          type: number
          format: double
          description: >-
            Position leverage multiplier from HL clearinghouseState; omitted
            when unavailable
          example: 10
        leverage_type:
          type: string
          enum:
            - cross
            - isolated
          description: HL leverage mode; omitted when unavailable
          example: cross
    UserMarginSummary:
      type: object
      required:
        - total_position_value
        - total_unrealized_pnl
        - total_realized_pnl
        - open_markets_count
      properties:
        total_position_value:
          type: number
          format: double
          example: 9690
        total_unrealized_pnl:
          type: number
          format: double
          example: 61.7
        total_realized_pnl:
          type: number
          format: double
          example: 150
        open_markets_count:
          type: integer
          example: 1
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code.
              example: RATE_LIMIT_EXCEEDED
            message:
              type: string
              description: Human-readable error description.
              example: Rate limit exceeded. Retry after 60 seconds.
  responses:
    QueryWindowExceeded:
      description: >-
        Time range exceeds the per-request maximum for the caller's tier. Narrow
        the from/to window and paginate.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: QUERY_WINDOW_EXCEEDED
              message: >-
                query time range exceeds maximum 24h0m0s per request for your
                tier — narrow your from/to window and paginate
    Unauthorized:
      description: Missing or invalid API key.
      headers:
        WWW-Authenticate:
          schema:
            type: string
            example: Bearer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: UNAUTHORIZED
              message: Invalid or missing API key
    Forbidden:
      description: Your tier does not include this feature.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: TIER_FORBIDDEN
              message: Export requires Builder tier or above
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            example: 60
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Rate limit exceeded. Retry after 60 seconds.
    InternalError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INTERNAL_ERROR
              message: An unexpected error occurred
    QueryTimeout:
      description: >-
        Query exceeded the 15-second execution limit. Narrow your time range,
        add a market filter, or reduce the limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: QUERY_TIMEOUT
              message: >-
                query took too long — narrow your time range, add a market
                filter, or reduce the limit
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Ironflow API key, format `if_xxxx`. Free keys at
        https://ironflow.sh/key. Optional on read endpoints.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.