curl "https://api.metastreams.dev/v1/positions?wallets=7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5&chains=solana&sort=pnl:desc" \
-H "Authorization: Bearer $API_KEY"
curl "https://api.metastreams.dev/v1/positions?wallets=7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5&lifecycle=closed" \
-H "Authorization: Bearer $API_KEY"
const url = new URL("https://api.metastreams.dev/v1/positions");
url.searchParams.set("wallets", "7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5");
url.searchParams.set("chains", "solana");
const headers = { Authorization: `Bearer ${process.env.API_KEY}` };
let cursor: string | null = null;
do {
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const page = await response.json();
for (const position of page.positions) console.log(position.token.symbol, position.value?.usd);
cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);
{
"asOf": 123,
"counts": {
"spot": {
"closed": 1,
"open": 1
}
},
"hasMore": true,
"positions": [
{
"avgCost": {
"native": "123.456789",
"usd": "123.456789"
},
"buyCount": 1,
"chain": "solana",
"fees": {
"native": "123.456789",
"usd": "123.456789"
},
"funding": {
"status": "unresolved",
"address": "<string>",
"amount": "123.456789",
"asset": "<string>",
"sourceKind": "cex",
"sourceLabel": "<string>",
"symbol": "<string>",
"timestamp": 123,
"tx": "<string>"
},
"holdings": "123.456789",
"holdingsPctSupply": "123.456789",
"invested": {
"native": "123.456789",
"usd": "123.456789"
},
"isStablecoin": true,
"isTransfer": true,
"lifetime": {
"buyCount": 1,
"cycleCount": 1,
"fees": {
"native": "123.456789",
"usd": "123.456789"
},
"invested": {
"native": "123.456789",
"usd": "123.456789"
},
"realizedPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"received": {
"native": "123.456789",
"usd": "123.456789"
},
"sellCount": 1,
"tokensBought": "123.456789",
"tokensSold": "123.456789",
"winningCycleCount": 1
},
"openedAt": 123,
"product": "spot",
"realizedPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"received": {
"native": "123.456789",
"usd": "123.456789"
},
"sellCount": 1,
"status": "open",
"token": {
"address": "<string>",
"chain": "solana",
"decimals": 1,
"image": "<string>",
"marketCap": {
"native": "123.456789",
"usd": "123.456789"
},
"name": "<string>",
"price": {
"native": "123.456789",
"usd": "123.456789"
},
"symbol": "<string>"
},
"tokensBought": "123.456789",
"tokensSold": "123.456789",
"updatedAt": 123,
"version": 123,
"walletAddress": "<string>",
"avgEntryMarketCap": {
"native": "123.456789",
"usd": "123.456789"
},
"avgExitMarketCap": {
"native": "123.456789",
"usd": "123.456789"
},
"closedAt": 123,
"identities": [
{
"source": "codex",
"avatar": "<string>",
"displayName": "<string>",
"farcaster": "<string>",
"handle": "<string>",
"telegram": "<string>",
"twitter": "<string>"
}
],
"pnlPct": "123.456789",
"totalPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"unrealizedPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"value": {
"native": "123.456789",
"usd": "123.456789"
}
}
],
"nextCursor": "<string>"
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}Positions
Page through the spot positions of one or more wallets, with cost basis and PnL.
curl "https://api.metastreams.dev/v1/positions?wallets=7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5&chains=solana&sort=pnl:desc" \
-H "Authorization: Bearer $API_KEY"
curl "https://api.metastreams.dev/v1/positions?wallets=7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5&lifecycle=closed" \
-H "Authorization: Bearer $API_KEY"
const url = new URL("https://api.metastreams.dev/v1/positions");
url.searchParams.set("wallets", "7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5");
url.searchParams.set("chains", "solana");
const headers = { Authorization: `Bearer ${process.env.API_KEY}` };
let cursor: string | null = null;
do {
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const page = await response.json();
for (const position of page.positions) console.log(position.token.symbol, position.value?.usd);
cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);
{
"asOf": 123,
"counts": {
"spot": {
"closed": 1,
"open": 1
}
},
"hasMore": true,
"positions": [
{
"avgCost": {
"native": "123.456789",
"usd": "123.456789"
},
"buyCount": 1,
"chain": "solana",
"fees": {
"native": "123.456789",
"usd": "123.456789"
},
"funding": {
"status": "unresolved",
"address": "<string>",
"amount": "123.456789",
"asset": "<string>",
"sourceKind": "cex",
"sourceLabel": "<string>",
"symbol": "<string>",
"timestamp": 123,
"tx": "<string>"
},
"holdings": "123.456789",
"holdingsPctSupply": "123.456789",
"invested": {
"native": "123.456789",
"usd": "123.456789"
},
"isStablecoin": true,
"isTransfer": true,
"lifetime": {
"buyCount": 1,
"cycleCount": 1,
"fees": {
"native": "123.456789",
"usd": "123.456789"
},
"invested": {
"native": "123.456789",
"usd": "123.456789"
},
"realizedPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"received": {
"native": "123.456789",
"usd": "123.456789"
},
"sellCount": 1,
"tokensBought": "123.456789",
"tokensSold": "123.456789",
"winningCycleCount": 1
},
"openedAt": 123,
"product": "spot",
"realizedPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"received": {
"native": "123.456789",
"usd": "123.456789"
},
"sellCount": 1,
"status": "open",
"token": {
"address": "<string>",
"chain": "solana",
"decimals": 1,
"image": "<string>",
"marketCap": {
"native": "123.456789",
"usd": "123.456789"
},
"name": "<string>",
"price": {
"native": "123.456789",
"usd": "123.456789"
},
"symbol": "<string>"
},
"tokensBought": "123.456789",
"tokensSold": "123.456789",
"updatedAt": 123,
"version": 123,
"walletAddress": "<string>",
"avgEntryMarketCap": {
"native": "123.456789",
"usd": "123.456789"
},
"avgExitMarketCap": {
"native": "123.456789",
"usd": "123.456789"
},
"closedAt": 123,
"identities": [
{
"source": "codex",
"avatar": "<string>",
"displayName": "<string>",
"farcaster": "<string>",
"handle": "<string>",
"telegram": "<string>",
"twitter": "<string>"
}
],
"pnlPct": "123.456789",
"totalPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"unrealizedPnl": {
"native": "123.456789",
"usd": "123.456789"
},
"value": {
"native": "123.456789",
"usd": "123.456789"
}
}
],
"nextCursor": "<string>"
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "<string>",
"details": "<unknown>"
}
}wallets takes 1 to 100 addresses, comma-separated. chains narrows the read to some chains.
Filters
lifecyclepicksopenpositions, the default, orclosedones.hideLowLiquiditydrops open positions in tokens with under $1,000 of liquidity. It istrueby default. Sendfalseto see every position.hideDustdrops open positions worth under $1. It isfalseby default.hideTransfersdrops positions that no buy opened, such as airdrops. It isfalseby default.chainandtokentogether read one token’s positions. Send both or neither.qkeeps tokens whose symbol or name contains it, in any letter case.
Sort
sort is field:asc or field:desc, where field is value, pnl, pnlPct, openedAt or closedAt. It is value:desc for open positions and closedAt:desc for closed ones.
Response
A page carriespositions, nextCursor, hasMore, asOf and counts. counts.spot holds the number of open and closed positions. It ignores lifecycle, the hide… switches, q, chain and token, so it stays the same across the pages of one read.
Each position prices its holdings at the token’s latest price. value, unrealizedPnl, totalPnl and pnlPct are null for a token with no market record, and pnlPct is also null for a position with no invested cost. version orders the changes to one position: keep the highest. The spot.positions stream uses the same rule.
Paging
Send backnextCursor exactly as you received it. A cursor holds the filters and sort of the read that made it. A cursor sent with other filters is refused with 400 VALIDATION_ERROR.
Errors
| Code | When |
|---|---|
UNSUPPORTED_CHAIN | A chains or chain value is not a supported slug |
VALIDATION_ERROR | No wallet or more than 100; a malformed wallet or token address; chain without token, or the reverse; an unknown sort; a cursor from other filters; or an unknown parameter |
curl "https://api.metastreams.dev/v1/positions?wallets=7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5&chains=solana&sort=pnl:desc" \
-H "Authorization: Bearer $API_KEY"
curl "https://api.metastreams.dev/v1/positions?wallets=7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5&lifecycle=closed" \
-H "Authorization: Bearer $API_KEY"
const url = new URL("https://api.metastreams.dev/v1/positions");
url.searchParams.set("wallets", "7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5");
url.searchParams.set("chains", "solana");
const headers = { Authorization: `Bearer ${process.env.API_KEY}` };
let cursor: string | null = null;
do {
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const page = await response.json();
for (const position of page.positions) console.log(position.token.symbol, position.value?.usd);
cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);
Authorizations
Send the API key as Authorization: Bearer <key>.
Query Parameters
Wallet addresses, CSV; one to 100.
Chains to scope to, CSV of the chain slugs. Absent means every spot chain.
Lifecycle to list; open when absent.
A position's current-cycle lifecycle.
open, closed Drop open positions whose token liquidity is below the low-liquidity floor.
Drop open positions whose remaining value is below the dust floor.
Drop positions never acquired through a buy.
Exact-token drill-down chain; requires token.
Exact-token drill-down address; requires chain.
Case-insensitive substring of the token symbol or name.
Sort field and direction, field:asc|desc. value:desc when absent, closedAt:desc for closed.
Rows per page, clamped to 1-100; 50 when absent.
1 <= x <= 100Opaque cursor from the previous page.
Response
One page of the named wallets' positions. The price-derived fields are null for a token without a price.
One page of the named wallets' positions.
Serve-time snapshot timestamp.
Per-product counts, taken before the lifecycle, display, search and drill-down filters.
Show child attributes
Show child attributes
Whether a next page exists.
Positions on this page.
Show child attributes
Show child attributes
Cursor for the next page; null on the last page.