Skip to content

One position's events

GET
/chains/solana/lp/position/{address}/events
curl --request GET \
--url 'https://api.getchord.xyz/v1/chains/solana/lp/position/example/events?limit=50'

Adds, removes, fee claims, opens and closes, newest first. A close row of a DLMM close_position2 or a Raydium CLMM position has no pool: the instruction does not carry it.

Liquidity on Solana from Chord’s own tape: adds, removes, fee claims, position opens and closes, decoded from the same blocks as the swaps. No key needed.

  • Venues. Meteora DLMM, Meteora DAMM v2, Raydium CLMM and PumpSwap. Orca Whirlpools, Raydium CPMM / v4 and Meteora dynamic pools are not read yet.
  • Since. since is when LP decoding started (2026-10-06 15:53 UTC); windows never start before it. A position opened earlier is partial: true and carries no PnL.
  • Fees are claimed fees. An LP’s fees appear when it claims them, so a window’s fees understate what the pool earned, and one claim can hold days of fees.
  • Positions. One row per position account (DLMM / DAMM v2 position, Raydium CLMM position NFT, PumpSwap LP token account), rebuilt from all its events. lower / upper are DLMM bin ids or Raydium CLMM ticks; null = full range. Realized PnL = withdrawn + fees claimed − deposited, only for a position seen from its open to its close with every amount priced. An open position’s current value is not in the events: none is given.
  • Amounts are whole tokens in the pool’s own order (mintA / mintB); USD is valued when the event happened. Each add / remove / claim is checked against the pool vaults’ balance changes in the same transaction (verified).
  • Addresses are base58 and case-sensitive, as on the other Solana routes.
address
required

Solana address, base58, case-sensitive. Never lower-case it.

string
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/

Position address, base58.

limit
integer
default: 50 >= 1 <= 200
before
integer
<= 4000000000

Exclusive cursor, Unix seconds: rows strictly older than it. Pass the last row’s ts for the next page. Several swaps can share a second, so a page boundary can skip the rest of that second: overlap with ts + 1 and drop rows whose tx you already have.

Media typeapplication/json
object
chain
string
Allowed value: solana
position

Solana address, base58, case-sensitive. Never lower-case it.

string
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/
events
Array<object>
object
ts

Unix time, seconds, UTC.

integer
slot
integer
signature

Transaction signature, base58.

string | null
/^[1-9A-HJ-NP-Za-km-z]{64,88}$/
dex

meteora_dlmm · meteora_damm_v2 · raydium_clmm · pumpswap.

string
kind
string
Allowed values: add remove fee open close
pool

Solana address, base58, case-sensitive. Never lower-case it.

string | null
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/
position

Solana address, base58, case-sensitive. Never lower-case it.

string | null
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/
owner

Solana address, base58, case-sensitive. Never lower-case it.

string | null
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/
amountA

Whole tokens.

number | null
amountB
number | null
usd
number | null
lower
integer | null
upper
integer | null
verified

True = matches the vault balance changes; null = could not be checked (shared vault, first deposit of a new pool)

boolean | null
nextBefore

Unix time, seconds, UTC.

integer | null
Example
{
"chain": "solana",
"position": "BJMyRCurUG45Lst2tZf2RMVbxwCopyWzxzHjgQz5ET6m",
"events": [
{
"ts": 1791303523,
"slot": 453953100,
"signature": "2e9A11ocj5EURDZJj7DYtFPQN7D62Xjdg1sW9aAyN8RJ4RmFBf9N73D29G11zJnJGc8HUnzQQ1WkH1KWduaggb5p",
"dex": "meteora_dlmm",
"kind": "close",
"pool": null,
"position": "BJMyRCurUG45Lst2tZf2RMVbxwCopyWzxzHjgQz5ET6m",
"owner": "GwBsj9GJdFrATQs1k9EYUyfaatKZDiViy8tkU9JP3TUL",
"amountA": null,
"amountB": null,
"usd": null,
"lower": null,
"upper": null,
"verified": null
},
{
"ts": 1791303523,
"slot": 453953100,
"signature": "2e9A11ocj5EURDZJj7DYtFPQN7D62Xjdg1sW9aAyN8RJ4RmFBf9N73D29G11zJnJGc8HUnzQQ1WkH1KWduaggb5p",
"dex": "meteora_dlmm",
"kind": "fee",
"pool": "5whNk71ua6aRtNKazDqEeAimmfzuX9epJcjh4isQKprn",
"position": "BJMyRCurUG45Lst2tZf2RMVbxwCopyWzxzHjgQz5ET6m",
"owner": "GwBsj9GJdFrATQs1k9EYUyfaatKZDiViy8tkU9JP3TUL",
"amountA": 0.03680336,
"amountB": 0,
"usd": 6.379515665752227,
"lower": null,
"upper": null,
"verified": true
}
],
"nextBefore": 1791303523
}

BAD_ADDRESS / BAD_LIMIT / BAD_CURSOR, or UNSUPPORTED_CHAIN (Solana is not public on this deployment and the request has no X-API-Key).

Media typeapplication/json

Every non-2xx body.

object
error
required

Stable machine code, e.g. INVALID_PARAMS, NOT_READY, RATE_LIMIT_EXCEEDED.

string
message

For humans.

string
statusCode

HTTP status, when included.

integer
Example
{
"error": "INVALID_PARAMS",
"message": "amount must be an integer string in the token's smallest unit"
}

Over the per-minute limit (RATE_LIMIT_EXCEEDED). Read X-RateLimit-Reset and back off.

Media typeapplication/json

Every non-2xx body.

object
error
required

Stable machine code, e.g. INVALID_PARAMS, NOT_READY, RATE_LIMIT_EXCEEDED.

string
message

For humans.

string
statusCode

HTTP status, when included.

integer
Example
{
"error": "INVALID_PARAMS",
"message": "amount must be an integer string in the token's smallest unit"
}

CHAIN_DATA_UNAVAILABLE (the Solana store did not answer in time) or CHAIN_NOT_ENABLED (the lane is off on this deployment). Retry later; do not treat it as an empty answer.

Media typeapplication/json

Every non-2xx body.

object
error
required

Stable machine code, e.g. INVALID_PARAMS, NOT_READY, RATE_LIMIT_EXCEEDED.

string
message

For humans.

string
statusCode

HTTP status, when included.

integer
Example
{
"error": "INVALID_PARAMS",
"message": "amount must be an integer string in the token's smallest unit"
}