Positions
Uniswap v4 liquidity positions on Ethereum (1) and Avalanche (43114). Every filter and sort the dashboard uses is on the same endpoint — there is no reduced public subset.
GET /v1/positions — 1 unit
A ranked, filtered, paginated page of positions.
curl -s "https://api.tickwise.xyz/v1/positions?chainId=1&sort=apr&limit=25" \
-H "KC-APIKey: $TICKWISE_API_KEY"Query parameters
Everything is optional. The defaults are not neutral — they are the visibility filters that keep spam and dust out of a ranking — so read the last three rows before concluding a position is missing.
| Parameter | Default | Meaning |
|---|---|---|
| chainId | all chains | 1 or 43114. Omitting it means every enabled chain, merged into one ranking — not Ethereum. An unregistered id is a 400. |
| protocol | uniswap-v4 | The only value served. uniswap-v3 is a 400 — see coverage. |
| sort | apr | One of apr, fee_apr, roi, pnl, underlying_value, age, nft_id_asc, nft_id_desc. Direction is part of the key, not a separate parameter: everything is descending except age and nft_id_asc. |
| page | 1 | 1-based. |
| limit | 20 | 1–100. A larger value is a 400 rather than a silent clamp. |
| status | open only | active, inactive or all — open, closed, or both. Unset behaves as active, because the older activeOnly flag it supersedes defaults to true; pass status=all to see closed positions. |
| range | all | in or out restricts to positions whose range currently contains the pool tick, or does not. |
| owner | — | A 0x-prefixed 40-hex EVM address, matched case-insensitively. Anything else is a 400 with a message naming the format. |
| poolId | — | Every position in one pool. |
| positionId | — | Exact match on a single id — the tokenId for an ERC-721 protocol. The other filters still apply, so a known id can still return nothing if it fails them. |
| minValueUsd | 0 | Floor on current underlying value. |
| minPoolTvl | 1000 | Floor on the pool’s TVL. Not zero — this is the one default that silently hides real positions in small pools. |
| includeRisky | false | Admits positions whose token has no corroborated on-chain market. Off by default because a ranking full of issuer-seeded spam pools is worthless. |
Response
A positions array and a pagination object. Each row carries rank, identity (positionId, tokenId, chainId, protocol, owner, poolId), both token snapshots, the range (tickLower, tickUpper, currentTick, tickSpacing, fee, liquidity, isActive), and four nested groups: amounts (token-denominated deposits, withdrawals, collected and uncollected fees, gas), metrics (apr, feeApr, roi, pnlUsd, underlyingValueUsd, pendingFeesUsd, ageSeconds, inRange), poolStats and pricing.
"pagination": { "page": 1, "limit": 20, "total": 0, "totalPages": 1, "hasMore": false }total is exact even in all-chains mode: the merge sums each shard’s own count rather than estimating from the page it returned.
Two fields on metrics exist to stop you reading a gap as a number. priceUnavailable marks a leg that could not be priced from any independent source, and unrealizableLeg marks one valued at zero because the claim exceeds the pool’s real exit liquidity. Neither is the same as a genuine zero.
The rate sorts — apr, fee_apr, roi — exclude closed and no-value positions rather than ranking them on a stale stored rate. pnl does not: realised PnL on a closed position is a real number and belongs in that ranking.
GET /v1/positions/{chainId}/{protocol}/{positionId} — 1 unit
One position in full, plus a transactions array the list endpoint does not carry: every mint, burn, collect and transfer with its token amounts, gas and per-transaction USD valuation.
curl -s "https://api.tickwise.xyz/v1/positions/1/uniswap-v4/12345" \
-H "KC-APIKey: $TICKWISE_API_KEY"An id that does not exist on that chain and protocol is a 404, not an empty object — and an id from the other chain will 404 here, which is the usual cause.
GET /v1/positions/{chainId}/{protocol}/{positionId}/charts — 10 units
Time series for one position. ?range=30d|60d|90d|180d|all, defaulting to 30d; an unrecognised value falls back to 30d rather than erroring, because the alternative is letting a typo fan out unbounded queries against a paid upstream.
curl -s "https://api.tickwise.xyz/v1/positions/1/uniswap-v4/12345/charts?range=90d" \
-H "KC-APIKey: $TICKWISE_API_KEY"The response carries poolPrice (daily OHLC plus both token prices), poolOverview (TVL, volume, fees, fees/TVL, both prices), liquidityDistribution (the tick histogram, with this position’s tickLower and tickUpper marked), positionSeries (PnL, asset value, APR, fee APR, divergence loss) and poolOverviewCurrent.
Three honest caveats. positionSeries is null when the series cannot be reconstructed, and null is not zero. poolOverview.divergence is always empty — the upstream schema does not carry it, and the key is present only so it can be filled without a breaking change. And this is the most expensive route we serve: it is a multi-query fan-out to an upstream we pay per call for, which is what the unit cost reflects.
Next
Pools — the same three shapes for pools. Units and limits — why a list costs more than a detail. Errors — every status code and whether retrying helps.