PearPear
API IntegrationTrade Activity

Open Position

List open basket positions.

List open basket positions with GET /positions (SDK: sdk.core.positions.list), or a smaller payload with GET /positions/lite (sdk.core.positions.openLite).

curl "https://pro-gateway.pearprotocol.io/positions?status=OPEN&limit=50" \
  -H "x-api-key: $PEAR_API_KEY" \
  -H "x-trade-account-id: $TRADE_ACCOUNT_ID"

Response

{
  "limit": 50,
  "positions": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "tradeAccountId": "9c1b2d3e-4f56-4789-a0b1-c2d3e4f56789",
      "connector": "hyperliquid",
      "status": "OPEN",
      "origin": "PEAR",
      "exposure":           { "0": "+0.0052", "1": "-0.1620" },
      "initialExposureUSD": { "0": "+500",    "1": "-500" },
      "entryPrices":        { "0": "96150.5", "1": "3086.2" },
      "totalFundingPayment": { "0": "-1.25",  "1": "+0.84" },
      "unstableHistoricalData": false,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T04:12:33.000Z"
    }
  ]
}

Reading the maps

exposure, initialExposureUSD, entryPrices, and totalFundingPayment are all keyed by instrument ID, not by ticker. In the example, "0" is BTC and "1" is ETH on Hyperliquid. Resolve the keys with GET /instruments/symbol-to-id-map, and see Instrument ID.

FieldMeaning
exposureSigned size per instrument. Positive is long, negative is short.
initialExposureUSDSigned USD size when the position opened. It does not move with adjustments.
entryPricesWeighted-average entry price per instrument, from the fills ledger. It does move with adjustments.
totalFundingPaymentCumulative funding in USD per instrument. Negative means you paid.
originPEAR if only Pear fills built the position, EXTERNAL if only outside fills did, PARTIAL for a mix.
unstableHistoricalDatatrue when a backfilled fill invalidated metrics derived from this position.
closedReasonPresent only on a closed position: USER, NETTED, or LIQUIDATED. Omitted, not null, when no reason was recorded.
closedAtWhen the position closed. Present only on a closed position.

Each position is basket-level: its PnL, entry, and size reflect the basket, not the exchange's per-asset view. See Synthetic Position.

Query parameters

ParameterValues
statusOPEN or CLOSED. Defaults to OPEN.
orderBy-createdAt (the default), createdAt, -updatedAt, or updatedAt.
limitDefault 20, maximum 2000.
cursorThe nextCursor of the previous page.
connectorAccepted and ignored. The venue comes from the trade account.

Omitting status returns open positions only. There is no "everything" default here. Ask for status=CLOSED to see closed rows.

status is single-valued in practice. Asking for both — ?status=OPEN,CLOSED — returns zero rows, because the two are combined as and rather than or. Make two calls, or read closed history from GET /positions/closed, which composes each closed trade properly.

orderBy also changes what the cursor means: the cursor is the value of whichever column the ordering uses, so a page taken under -createdAt cannot be continued under -updatedAt. Keep orderBy fixed for the whole walk.

nextCursor here is a bare timestamp with no tiebreaker. Two positions created in the same instant sit on a page boundary that can drop or repeat a row. GET /positions/closed uses a compound cursor and does not have this problem.

The lite list

GET /positions/lite takes no query parameters, does not page, and returns only open positions:

{
  "positions": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "positionKey": "9f2c4a7e10b3486d5c8e1a04b7d92f63c05e8a1b7d34f62089ac5e1f30b74d28",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "exposure": { "0": "+0.0052", "1": "-0.1620" },
      "status": "OPEN",
      "origin": "PEAR",
      "connector": "hyperliquid"
    }
  ]
}

positionKey is a hex string derived from the trade account and the basket's instruments, so the same basket on the same account always produces the same key. It is the field that joins a position to the schedules and ladders working on the same basket. It appears on no other position read. Treat it as an opaque identifier. See Position Key.

Use this endpoint when you are polling for state changes and do not need entry prices or funding, both of which the full list computes on every request.

Live updates

Subscribe to user_events on the WebSocket and read position.created, position.updated, and position.closed instead of polling. Closed positions stay readable through GET /positions/closed.

On this page