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

# Get account value and PnL snapshots

> Read hourly history and latest indexed account value and PnL in USDC.

Returns stored hourly snapshots and a live indexed point for the specified account, without combining
its subaccounts. Account value and PnL share this endpoint; there is no separate
PnL snapshot endpoint. No request body is accepted.

<ParamField path="userId" type="string" required>
  Unsigned 64-bit Kuru account ID as a decimal string.
</ParamField>

<ParamField query="timeframe" type="string" default="7d">
  One of `1d`, `7d`, or `30d`. History starts one timeframe before the current
  UTC hour (exclusive). A live point extends the response through the latest
  indexed balance/trade source time or last stored hourly boundary, whichever
  is later (inclusive). This does not prove ingestion is caught up to the chain.
</ParamField>

All monetary values are exact decimal strings in **USDC × 10¹⁸**:
`"1000000000000000000"` represents 1 USDC. PnL can be negative. Timestamps
are Unix **seconds**, not milliseconds.

<ResponseField name="feedEpoch" type="string | null" required>
  Materialization feed epoch, or `null` before initialization. `"0"` is valid.
  This is not a snapshot timestamp or a WebSocket replay sequence.
</ResponseField>

<ResponseField name="data.totalAccountValueX18" type="string | null" required>
  Latest indexed account value, including free and reserved token balances,
  valued at latest indexed prices. Does not include passive LP positions.
</ResponseField>

<ResponseField name="data.performance" type="array" required>
  Tuples `[accountValueX18, pnlX18, timestamp]`, oldest first. At most
  24, 168, or 720 hourly points plus one live point. The final point need not
  be hour-aligned; duplicate timestamps are not returned.
</ResponseField>

<ResponseField name="data.relativePnlX18" type="string | null" required>
  Latest cumulative total PnL minus start-hour cumulative total PnL. An amount,
  not a percentage. Null when a required boundary value is unavailable.
</ResponseField>

<ResponseField name="data.relativeVolumeX18" type="string | null" required>
  The user's maker/taker trade notional over the selected window, converted to
  USDC at each trade's historical quote-token price. Excludes transfers.
</ResponseField>

### Performance tuple

| Position | Field | Type | Meaning |
| - | - | - | - |
| 0 | `accountValueX18` | string or null | Total token-balance value in USDC at the tuple timestamp. |
| 1 | `pnlX18` | string or null | Cumulative realized plus unrealized PnL across markets, not just that hour's PnL. |
| 2 | `timestamp` | integer | Unix seconds. The live point uses indexed source time or the last stored hourly boundary, whichever is later—not request time. |

Historical hours are stored snapshots; missing hours are not fabricated. Unknown
valuations or unavailable snapshot values are `null`, not zero. A nonzero
holding without a supported valuation market makes the account total unknown.
For a configured valuation market with no trade yet, the valuation uses zero.

The single live point reads current free and reserved balances directly from
`user_token_balances` and current inventory, cost basis and realized PnL from
`user_market_positions`, valued using latest indexed trade prices. It does not
reconstruct live state from append-only events or hourly snapshots. History,
current state, prices and feed epoch are read in one consistent database snapshot.

With no snapshots but indexed account state, the live point is still returned.
Accounts without history or state return `performance: []` and null account/PnL
summaries. Volume includes trades through the indexed source cutoff and is `"0"`
when none exist. Historical values are never repriced using current prices.

Illustrative response:

<ResponseExample>
  ```json 200 theme={null}
  {
    "feedEpoch": "0",
    "data": {
      "totalAccountValueX18": "2500000000000000000000",
      "performance": [
        ["2500000000000000000000", "-5000000000000000000", 1791763200]
      ],
      "relativePnlX18": "-5000000000000000000",
      "relativeVolumeX18": "100000000000000000000"
    }
  }
  ```
</ResponseExample>

Unknown users return HTTP 200 with empty history, not 404. Invalid IDs,
unsupported timeframes, and unknown query parameters return 400. Unsupported
methods, including POST, return 405.


## OpenAPI

````yaml api/data-source/openapi.yaml GET /api/v1/users/{userId}/account-value-snapshots
openapi: 3.1.0
info:
  title: Kuru Data Source API
  version: 1.0.0
servers:
  - url: https://api.mainnet.kuru.io
    description: Monad mainnet (default)
  - url: https://api.testnet.kuru.io
    description: Monad testnet
security: []
paths:
  /api/v1/users/{userId}/account-value-snapshots:
    get:
      summary: Get account value snapshots
      operationId: getAccountValueSnapshots
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: integer
        - name: timeframe
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Account value snapshots

````

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