@solana-tracker/data-api
v0.3.2
Published
Official Solana Tracker Data API client for accessing Solana Data
Downloads
10,463
Maintainers
Readme
Solana Tracker - Data API SDK
Official JavaScript/TypeScript client for the Solana Tracker Data API.
Features (Summary)
- Full TypeScript support with detailed interfaces for all API responses
- PnL v2: REST endpoints under
/v2/pnl(leaderboards, token traders, wallet analytics, wallet-summary batch, batch positions) and Datastream roomspnl:{wallet},pnl:{wallet}:{token},pnl:{wallet}:summary— scoped to meme / tradable tokens, not SOL. IncludespnlMode(strict/adjusted/raw), unified wallet identity (KOL, bot, pool, developer, hacker, spam-dusting, exchange, SNS primary.soldomain, platform tags), always-on token enrichment, opt-in holder enrichment, full position rows onwallet/positionsandtokens/:mint/traders, andsummary.timing.avgHoldTimeSecson the wallet overview - Jupiter DCA: REST endpoints under
/dca/*(programs, wallet orders, single order, token flow/buyers/sellers/users, trading pair) and Datastream roomsdca:jupiter[:event][:scope]for opened, filled, closed, deposit, withdraw, collected_fee, and live position snapshots — scoped per token, wallet, or DCA account - Comprehensive coverage of all Solana Tracker Data API endpoints
- Real-time data streaming via WebSocket (Datastream)
- Built-in error handling with specific error types
- Compatible with both Node.js and browser environments
- Enhanced search with 60+ filter parameters including holder distribution, social media, fees, Coin Communities chat activity, and more
- Cursor-based pagination for efficient deep searches
- Top performers endpoint - get the best performing tokens launched today
- Token filtering for overview endpoints (Memescope / Pulse overview) (graduated, graduating, latest)
- Paginated token holders endpoint for efficient holder data retrieval (5000 per page limit)
- Aggregated price updates across all pools for a token via WebSocket (min, median, max, average, top pools setc.)
- Smart primary pool routing - automatically switches to new main pool (price-by-token, token:{token}:primary rooms)
- (Global Fees) Platform and network fees tracking via WebSocket and API
- Developer/creator holdings tracking via WebSocket
- Top 10 holders monitoring with real-time percentage updates
- Live stats subscriptions for tokens and pools
- Primary pool subscriptions for token updates
- Wallet balance subscription API
- Snipers and insiders tracking via WebSocket
- Support for all pool types including launchpad and meteora curve pools (Shows which platform token is released on, Moonshot, Bonk, Jupiter Studio etc)
Installation
Install the package using npm:
npm install @solana-tracker/data-apiOr with yarn:
yarn add @solana-tracker/data-apiQuick Start
import { Client } from '@solana-tracker/data-api';
// Initialize the client with your API key
const client = new Client({
apiKey: 'YOUR_API_KEY',
});
// Fetch token information
const fetchTokenInfo = async () => {
try {
const tokenInfo = await client.getTokenInfo(
'4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R'
);
console.log('Token info:', tokenInfo);
} catch (error) {
console.error('Error:', error);
}
};
fetchTokenInfo();Examples
Runnable samples live in the examples/ folder. Copy a file, set your API key or WebSocket URL, and run with npx ts-node (or compile with TypeScript) after installing the package.
| File | What it demonstrates |
|------|----------------------|
| examples/tokens.ts | Token info, holders, trending, search |
| examples/prices.ts | Price and multi-price endpoints |
| examples/wallets.ts | Wallet balances and portfolio |
| examples/trades.ts | Token and pool trades |
| examples/charts.ts | OHLCV and holder/snipers charts |
| examples/stats.ts | Token and pool stats |
| examples/bundlers.ts | Bundler-related API usage |
| examples/profit-loss.ts | Legacy PnL endpoints (getWalletPnL, getTokenPnL, first buyers, top traders) |
| examples/pnl-v2.ts | PnL v2 REST: leaderboards, token traders, wallet overview/history/performance/risk/chart, wallet-summary batch, batch positions (examples use a meme mint, not SOL) |
| examples/pnl-v2-datastream.ts | PnL v2 Datastream: subscribe.pnl.position, .wallet, .summary with tradeUpdate, balanceUpdate, priceUpdate, and wallet summary payloads |
| examples/dca.ts | Jupiter DCA REST: getDcaPrograms, wallet/orders, single order, token flow/buyers/sellers/users, pair |
| examples/dca-datastream.ts | Jupiter DCA Datastream: subscribe.dca.all, .opened, .filled, .closed, .deposit, .withdraw, .collectedFee, .position, .token().buyers()/sellers(), .wallet, .order |
| examples/datastream.ts | WebSocket subscriptions (prices, txs, stats, volume, etc.) |
| examples/utils.ts | Shared helpers (handleError, formatting) |
What's New
Version Updates
Latest Features:
Multiple Markets & Launchpads:
marketandlaunchpadnow accept comma-separated values or arrays on both/searchand/deployer:// Search across multiple markets const results = await client.searchTokens({ market: ['raydium', 'orca', 'pumpfun'], launchpad: ['pumpfun', 'boop'], minLiquidity: 10000, }); // Filter deployer tokens by market and launchpad const deployerTokens = await client.getTokensByDeployer('walletAddress', { market: ['raydium', 'pumpfun'], launchpad: 'pumpfun', });Full Token Format:
format=fullreturns results as full token objects (same shape as/tokens/:token) on both/searchand/deployer:// Search with full token details (max limit: 100) const fullResults = await client.searchTokens({ query: 'TRUMP', format: 'full', limit: 50, }); // fullResults.data items are TokenDetailResponse objects (token, pools, events, risk, etc.) // Deployer tokens with full details const fullDeployer = await client.getTokensByDeployer('walletAddress', { format: 'full', limit: 50, });Deployer Endpoint Enhancements: The
/deployerendpoint now supportslaunchpad,market, andformatfilters via a params object (backward compatible with positional args).Pool Creation Data: New and graduated pool messages now include
creationdata (creator, created_tx, created_time) inside the pool object.Top Performers Endpoint: Get the best performing tokens launched today:
const topPerformers = await client.getTopPerformers('1h'); // Valid timeframes: '5m', '15m', '30m', '1h', '6h', '12h', '24h' console.log('Top performers:', topPerformers.map(t => t.token.symbol));Enhanced Token Overview with Advanced Filtering:
- Filter latest, graduating, and graduated tokens by liquidity, market cap, risk score, and specific markets
- Support for spam reduction and holder count filtering
- Backward compatible with simple limit parameter
Paginated Token Holders:
- Efficient pagination through large holder lists
- Support for up to 5000 holders per request
- Cursor-based navigation for seamless data retrieval
Aggregated Price Updates (WebSocket):
- Get accurate price data aggregated across all pools
- Median, average, min, and max prices
- Top pools by liquidity automatically identified
- Replaces deprecated
price.token()method
Smart Primary Pool Routing:
- Automatic switching to new primary pools when liquidity migrates
- Works for both
token:primaryandprice-by-tokensubscriptions - No code changes needed - happens automatically
Enhanced Search with 60+ Filters:
- Holder distribution filters (top10, dev, insiders, snipers)
- Social media filters (twitter, telegram, discord, etc.)
- Fees filters (total fees, trading fees, priority tips)
- Volume filters across multiple timeframes
- Token characteristic filters (LP burn, authorities, curve percentage)
- Cursor-based pagination for efficient deep searches
Live Stats Subscriptions: Subscribe to real-time statistics for tokens and pools across all timeframes (1m, 5m, 15m, 30m, 1h, 4h, 24h) using
.stats.token()and.stats.pool()methodsTotal Stats Rooms: Subscribe to direct total stats objects with
.stats.total.token()and.stats.total.pool()Volume Rooms: Subscribe to high-frequency USD volume aggregation via
.volume.pool()and.volume.token()Developer Holdings Tracking: Monitor developer/creator wallet holdings in real-time with
.dev.holding()methodTop 10 Holders Monitoring: Track the top 10 holders and their combined percentage of token supply with
.top10()methodGlobal Fees Tracking: Monitor platform and network fees via WebSocket and API
Real-Time Data Streaming (Premium plan or higher only)
The library includes a Datastream class for real-time data updates with an improved, intuitive API:
import { Datastream } from '@solana-tracker/data-api';
// Initialize the Datastream with your API key
const dataStream = new Datastream({
wsUrl: 'YOUR_WS_URL',
});
// Connect to the WebSocket server
dataStream.connect();
// Handle connection events
dataStream.on('connected', () => console.log('Connected to datastream'));
dataStream.on('disconnected', () =>
console.log('Disconnected from datastream')
);
dataStream.on('error', (error) => console.error('Datastream error:', error));
// Example 1: Subscribe to latest tokens with chained listener
dataStream.subscribe.latest().on((tokenData) => {
console.log('New token created:', tokenData.token.name);
});
// Example 2: NEW - Get accurate aggregated price across all pools
const tokenAddress = '6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN'; // TRUMP token
dataStream.subscribe.price.aggregated(tokenAddress).on((priceData) => {
console.log(`Aggregated price: $${priceData.aggregated.median}`);
console.log(`Price range: $${priceData.aggregated.min} - $${priceData.aggregated.max}`);
console.log(`Active pools: ${priceData.aggregated.poolCount}`);
console.log('Top pools by liquidity:', priceData.topPools);
});
// Example 3: Subscribe to primary pool updates (automatically switches to new main pool)
dataStream.subscribe.token(tokenAddress).primary().on((poolUpdate) => {
console.log('Primary pool update (automatically switches to new main pool):');
console.log(`Price: $${poolUpdate.price.usd}`);
console.log(`Liquidity: $${poolUpdate.liquidity.usd}`);
console.log(`Pool: ${poolUpdate.poolId}`);
});
// Example 4: Subscribe to token transactions with stored subscription reference
const txSubscription = dataStream.subscribe.tx
.token(tokenAddress)
.on((transaction) => {
console.log(`Transaction type: ${transaction.type}`);
console.log(`Amount: ${transaction.amount}`);
console.log(`Price: $${transaction.priceUsd}`);
});
// Later, unsubscribe from transactions
txSubscription.unsubscribe();
// Example 5: Monitor holder count for a token
dataStream.subscribe.holders(tokenAddress).on((holderData) => {
console.log(`Total holders: ${holderData.total}`);
});
// Example 6: Watch for wallet transactions
const walletAddress = 'YourWalletAddressHere';
dataStream.subscribe.tx.wallet(walletAddress).on((walletTx) => {
console.log(`${walletTx.type === 'buy' ? 'Bought' : 'Sold'} token`);
console.log(`Volume: ${walletTx.volume} USD`);
});
// Example 7: Subscribe to curve percentage updates
dataStream.subscribe.curvePercentage('pumpfun', 30).on((data) => {
console.log(`Token ${data.token.symbol} reached 30% on Pump.fun`);
console.log(`Market cap: ${data.pools[0].marketCap.usd}`);
});
// Different markets and percentages
dataStream.subscribe.curvePercentage('meteora-curve', 75).on((data) => {
console.log(`Meteora token at 75%: ${data.token.name}`);
});
// Example 8: NEW - Monitor snipers for a token
dataStream.subscribe.snipers(tokenAddress).on((sniperUpdate) => {
console.log(`Sniper wallet: ${sniperUpdate.wallet}`);
console.log(`Token amount: ${sniperUpdate.tokenAmount.toLocaleString()}`);
console.log(`Percentage: ${sniperUpdate.percentage.toFixed(2)}%`);
console.log(
`Total snipers hold: ${sniperUpdate.totalSniperPercentage.toFixed(2)}%`
);
});
// Example 9: NEW - Monitor insiders for a token
dataStream.subscribe.insiders(tokenAddress).on((insiderUpdate) => {
console.log(`Insider wallet: ${insiderUpdate.wallet}`);
console.log(`Token amount: ${insiderUpdate.tokenAmount.toLocaleString()}`);
console.log(`Percentage: ${insiderUpdate.percentage.toFixed(2)}%`);
console.log(
`Total insiders hold: ${insiderUpdate.totalInsiderPercentage.toFixed(2)}%`
);
});
// Example 10: NEW - Monitor wallet balance changes (new API location)
const walletAddress = 'YourWalletAddressHere';
// Watch all token balance changes for a wallet
dataStream.subscribe
.wallet(walletAddress)
.balance()
.on((balanceUpdate) => {
console.log(`Balance update for wallet ${balanceUpdate.wallet}`);
console.log(`Token: ${balanceUpdate.token}`);
console.log(`New balance: ${balanceUpdate.amount}`);
});
// Watch specific token balance for a wallet
dataStream.subscribe
.wallet(walletAddress)
.tokenBalance('tokenMint')
.on((balanceUpdate) => {
console.log(`Token balance changed to: ${balanceUpdate.amount}`);
});
// Example 11: NEW - Subscribe to live stats for tokens and pools
// Get real-time statistics across all timeframes (1m, 5m, 15m, 30m, 1h, 4h, 24h)
dataStream.subscribe.stats.token(tokenAddress).on((stats) => {
console.log('Live token stats update:');
// Access specific timeframe stats
if (stats['24h']) {
console.log('24h Stats:');
console.log(` Volume: $${stats['24h'].volume.total.toLocaleString()}`);
console.log(` Buys: ${stats['24h'].buys}, Sells: ${stats['24h'].sells}`);
console.log(` Unique wallets: ${stats['24h'].wallets}`);
console.log(
` Price change: ${stats['24h'].priceChangePercentage.toFixed(2)}%`
);
}
if (stats['1h']) {
console.log('1h Stats:');
console.log(
` Buyers: ${stats['1h'].buyers}, Sellers: ${stats['1h'].sellers}`
);
console.log(` Buy volume: $${stats['1h'].volume.buys.toLocaleString()}`);
console.log(` Sell volume: $${stats['1h'].volume.sells.toLocaleString()}`);
}
// Iterate through all available timeframes
Object.entries(stats).forEach(([timeframe, data]) => {
console.log(
`${timeframe}: ${data.transactions} txns, ${data.wallets} wallets`
);
});
});
// Subscribe to live stats for a specific pool
dataStream.subscribe.stats.pool('poolId').on((stats) => {
console.log('Pool stats update:');
if (stats['5m']) {
console.log('Last 5 minutes:');
console.log(` Transactions: ${stats['5m'].transactions}`);
console.log(` Volume: $${stats['5m'].volume.total.toLocaleString()}`);
console.log(` Price: $${stats['5m'].price}`);
}
});
// Example 12: NEW - Monitor developer/creator holdings for a token
dataStream.subscribe
.token(tokenAddress)
.dev.holding()
.on((devUpdate) => {
console.log(`Developer ${devUpdate.creator} holdings update:`);
console.log(` Amount: ${devUpdate.amount}`);
console.log(` Percentage: ${devUpdate.percentage.toFixed(4)}%`);
console.log(` Previous: ${devUpdate.previousPercentage.toFixed(4)}%`);
const change = devUpdate.percentage - devUpdate.previousPercentage;
console.log(` Change: ${change > 0 ? '+' : ''}${change.toFixed(4)}%`);
});
// Example 13: NEW - Monitor top 10 holders for a token
dataStream.subscribe
.token(tokenAddress)
.top10()
.on((top10Update) => {
console.log(
`Top 10 holders control ${top10Update.totalPercentage.toFixed(2)}% of supply`
);
if (top10Update.previousPercentage !== null) {
const change =
top10Update.totalPercentage - top10Update.previousPercentage;
console.log(
`Change from previous: ${change > 0 ? '+' : ''}${change.toFixed(2)}%`
);
}
console.log('Top 10 holders:');
top10Update.holders.forEach((holder, index) => {
console.log(
` #${index + 1}: ${holder.address.slice(0, 8)}... - ${holder.percentage.toFixed(2)}%`
);
});
});
// Example 14: NEW - Monitor platform and network fees for a token
dataStream.subscribe
.token(tokenAddress)
.fees()
.on((feesUpdate) => {
console.log(
`Total fees accumulated: ${feesUpdate.fees.total.toFixed(6)} SOL`
);
console.log(
`MEV/Priority tips: ${feesUpdate.fees.totalTips.toFixed(6)} SOL`
);
console.log('Transaction tx', feesUpdate.tx);
// Individual platforms (not all will be present)
if (feesUpdate.fees.photon !== undefined) {
console.log(`Photon fees: ${feesUpdate.fees.photon.toFixed(6)} SOL`);
}
if (feesUpdate.fees.axiom !== undefined) {
console.log(`Axiom fees: ${feesUpdate.fees.axiom.toFixed(6)} SOL`);
}
if (feesUpdate.fees.bullx !== undefined) {
console.log(`BullX fees: ${feesUpdate.fees.bullx.toFixed(6)} SOL`);
}
if (feesUpdate.fees.jito !== undefined) {
console.log(`Jito tips: ${feesUpdate.fees.fees.jito.toFixed(6)} SOL`);
}
if (feesUpdate.fees.network !== undefined) {
console.log(`Network fees: ${feesUpdate.fees.network.toFixed(6)} SOL`);
}
});Available subscription methods:
// Token and pool updates
dataStream.subscribe.latest(); // Latest tokens and pools
dataStream.subscribe.token(tokenAddress); // Token changes (all pools - default)
dataStream.subscribe.token(tokenAddress).all(); // Token changes (all pools - explicit)
dataStream.subscribe.token(tokenAddress).primary(); // Token changes (primary pool only - auto-switches to new main pool)
// Developer and Top Holders tracking
dataStream.subscribe.token(tokenAddress).dev.holding(); // Developer holdings updates
dataStream.subscribe.token(tokenAddress).top10(); // Top 10 holders updates
dataStream.subscribe.token(tokenAddress).fees();
dataStream.subscribe.pool(poolId); // Pool changes
// Price updates
dataStream.subscribe.price.aggregated(tokenAddress); // NEW: Aggregated price across all pools (RECOMMENDED)
dataStream.subscribe.price.token(tokenAddress); // DEPRECATED: Token price (auto-switches to new main pool)
dataStream.subscribe.price.allPoolsForToken(tokenAddress); // All price updates for a token
dataStream.subscribe.price.pool(poolId); // Pool price
// Transactions
dataStream.subscribe.tx.token(tokenAddress); // Token transactions
dataStream.subscribe.tx.pool(tokenAddress, poolId); // Pool transactions
dataStream.subscribe.tx.wallet(walletAddress); // Wallet transactions
// Wallet balance updates (NEW location)
dataStream.subscribe.wallet(walletAddress).balance(); // All token balance changes
dataStream.subscribe.wallet(walletAddress).tokenBalance(tokenAddress); // Specific token balance
// Live statistics (NEW)
dataStream.subscribe.stats.token(tokenAddress); // Live stats for a token
dataStream.subscribe.stats.pool(poolId); // Live stats for a pool
dataStream.subscribe.stats.total.token(tokenAddress); // Direct total stats object for a token
dataStream.subscribe.stats.total.pool(poolId); // Direct total stats object for a pool
// Volume rooms (NEW)
dataStream.subscribe.volume.pool(poolId); // USD volume per pool (flush ~50ms)
dataStream.subscribe.volume.token(tokenAddress); // USD volume per token (cross-pool deduplicated, flush ~50ms)
// PnL v2 (meme / tradable mints — not SOL)
dataStream.subscribe.pnl.position(walletAddress, tokenAddress); // tradeUpdate + balanceUpdate + throttled priceUpdate for one position
dataStream.subscribe.pnl.wallet(walletAddress); // all position updates for wallet
dataStream.subscribe.pnl.summary(walletAddress); // wallet summary aggregates
// Pump.fun stages
dataStream.subscribe.graduating(); // Graduating tokens
dataStream.subscribe.graduated(); // Graduated tokens
// Metadata and holders
dataStream.subscribe.metadata(tokenAddress); // Token metadata
dataStream.subscribe.holders(tokenAddress); // Holder updates
// Curve percentage updates
dataStream.subscribe.curvePercentage(market, percentage); // Market options: 'launchpad', 'pumpfun', 'boop', 'meteora-curve'
// Snipers and Insiders tracking
dataStream.subscribe.snipers(tokenAddress); // Track sniper wallets
dataStream.subscribe.insiders(tokenAddress); // Track insider walletsEach subscription method returns a response object with:
room: The subscription channel nameon(): Method to attach a listener with proper TypeScript types- Returns an object with
unsubscribe()method for easy cleanup
- Returns an object with
Smart Primary Pool Routing
The token:primary and price-by-token subscriptions now automatically switch to the new primary pool when liquidity migrates:
// Subscribe to primary pool - automatically switches when main pool changes
dataStream.subscribe.token(tokenAddress).primary().on((poolUpdate) => {
console.log('Primary pool update (auto-switches to new main pool):');
console.log(`Pool ID: ${poolUpdate.poolId}`);
console.log(`Liquidity: $${poolUpdate.liquidity.usd}`);
// When liquidity moves to a new pool, you'll automatically get updates from the new pool
});
// DEPRECATED: price.token() also auto-switches but use price.aggregated() instead
dataStream.subscribe.price.token(tokenAddress).on((priceData) => {
console.log('Price from main pool (auto-switches):');
console.log(`Price: $${priceData.price}`);
console.log(`Pool: ${priceData.pool}`);
});
// RECOMMENDED: Use aggregated price for accurate data across all pools
dataStream.subscribe.price.aggregated(tokenAddress).on((priceData) => {
console.log('Aggregated price data:');
console.log(`Median: $${priceData.aggregated.median}`);
console.log(`Average: $${priceData.aggregated.average}`);
console.log(`Range: $${priceData.aggregated.min} - $${priceData.aggregated.max}`);
});Migration Guide for Balance Updates
The wallet balance subscriptions have been moved to a more intuitive location:
// OLD (deprecated - will show warning)
dataStream.subscribe.tx.wallet(walletAddress).balance().on(callback);
dataStream.subscribe.tx
.wallet(walletAddress)
.tokenBalance(tokenAddress)
.on(callback);
// NEW (recommended)
dataStream.subscribe.wallet(walletAddress).balance().on(callback);
dataStream.subscribe
.wallet(walletAddress)
.tokenBalance(tokenAddress)
.on(callback);
// Note: Wallet transactions remain under .tx namespace
dataStream.subscribe.tx.wallet(walletAddress).on(callback); // Still the correct way for transactionsMigration Guide for Price Updates
The price.token() method is deprecated in favor of price.aggregated():
// OLD (deprecated - will show warning)
dataStream.subscribe.price.token(tokenAddress).on(callback);
// NEW (recommended - more accurate across all pools)
dataStream.subscribe.price.aggregated(tokenAddress).on(callback);
// For specific pool price (still valid)
dataStream.subscribe.price.pool(poolId).on(callback);WebSocket Data Stream
The Datastream class provides real-time access to Solana Tracker data:
Events
The Datastream extends the standard EventEmitter interface, allowing you to listen for various events:
// Connection events
dataStream.on('connected', () => console.log('Connected to WebSocket server'));
dataStream.on('disconnected', (socketType) =>
console.log(`Disconnected: ${socketType}`)
);
dataStream.on('reconnecting', (attempt) =>
console.log(`Reconnecting: attempt ${attempt}`)
);
dataStream.on('error', (error) => console.error('Error:', error));
// Data events - Standard approach
dataStream.on('latest', (data) => console.log('New token:', data));
dataStream.on(`price:aggregated:${tokenAddress}`, (data) =>
console.log('Aggregated price update:', data)
); // NEW
dataStream.on(`price-by-token:${tokenAddress}`, (data) =>
console.log('Price update (deprecated):', data)
);
dataStream.on(`transaction:${tokenAddress}`, (data) =>
console.log('New transaction:', data)
);
dataStream.on(`token:${tokenAddress}`, (data) =>
console.log('Token update (all pools):', data)
);
dataStream.on(`token:${tokenAddress}:primary`, (data) =>
console.log('Token update (primary pool - auto-switches):', data)
);
dataStream.on(`sniper:${tokenAddress}`, (data) =>
console.log('Sniper update:', data)
);
dataStream.on(`insider:${tokenAddress}`, (data) =>
console.log('Insider update:', data)
);
dataStream.on(`stats:token:${tokenAddress}`, (data) =>
console.log('Token stats:', data)
);
dataStream.on(`stats:pool:${poolId}`, (data) =>
console.log('Pool stats:', data)
);
// Developer and top holders events
dataStream.on(`dev_holding:${tokenAddress}`, (data) =>
console.log('Dev holding update:', data)
);
dataStream.on(`top10:${tokenAddress}`, (data) =>
console.log('Top 10 holders update:', data)
);
dataStream.on(`fees:${tokenAddress}`, (data) => console.log('Fees update:', data));
// New approach - Chain .on() directly to subscription
dataStream.subscribe.latest().on((data) => console.log('New token:', data));
dataStream.subscribe.price
.aggregated(tokenAddress)
.on((data) => console.log('Aggregated price:', data)); // NEW
dataStream.subscribe.tx
.token(tokenAddress)
.on((data) => console.log('Transaction:', data));
dataStream.subscribe
.snipers(tokenAddress)
.on((data) => console.log('Sniper:', data));
dataStream.subscribe
.insiders(tokenAddress)
.on((data) => console.log('Insider:', data));
dataStream.subscribe.stats
.token(tokenAddress)
.on((data) => console.log('Stats:', data));
dataStream.subscribe.stats
.pool(poolId)
.on((data) => console.log('Pool stats:', data));API Documentation
The library provides methods for all endpoints in the Solana Tracker Data API.
Token Endpoints
// Get token information
const tokenInfo = await client.getTokenInfo('tokenAddress');
// Get token by pool address
const tokenByPool = await client.getTokenByPool('poolAddress');
// Get token holders
const tokenHolders = await client.getTokenHolders('tokenAddress');
// NEW: Get token holders with pagination (up to 5000 per request)
const paginatedHolders = await client.getTokenHoldersPaginated(
'tokenAddress',
100, // limit (optional, default: 100, max: 5000)
'cursorFromPreviousRequest' // cursor (optional)
);
// Access the data
console.log('Total holders:', paginatedHolders.total);
console.log('Holders in this page:', paginatedHolders.accounts.length);
console.log('Has more pages:', paginatedHolders.hasMore);
// Get next page
if (paginatedHolders.hasMore) {
const nextPage = await client.getTokenHoldersPaginated(
'tokenAddress',
100,
paginatedHolders.cursor
);
}
// Get top token holders
const topHolders = await client.getTopHolders('tokenAddress');
// Get all-time high price for a token
const athPrice = await client.getAthPrice('tokenAddress');
// Get tokens by deployer wallet
const deployerTokens = await client.getTokensByDeployer('walletAddress');
// Get tokens by deployer with filters (NEW)
const filteredDeployerTokens = await client.getTokensByDeployer('walletAddress', {
page: 1,
limit: 100,
market: ['raydium', 'pumpfun'],
launchpad: 'pumpfun',
});
// Get deployer tokens with full token details (NEW)
const fullDeployerTokens = await client.getTokensByDeployer('walletAddress', {
format: 'full',
limit: 50, // max 100 when format=full
});
// fullDeployerTokens.tokens are TokenDetailResponse objects
// Get latest tokens
const latestTokens = await client.getLatestTokens(100);
// Get information about multiple tokens (UPDATED: Now returns MultiTokensResponse)
const multipleTokens = await client.getMultipleTokens([
'So11111111111111111111111111111111111111112',
'4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
]);
// Access tokens like: multipleTokens.tokens['tokenAddress']
// Get trending tokens
const trendingTokens = await client.getTrendingTokens('1h');
// Get top performing tokens by price change percentage
const topPerformers = await client.getTopPerformers('1h');
// Valid timeframes: '5m', '15m', '30m', '1h', '6h', '12h', '24h'
// Get tokens by volume
const volumeTokens = await client.getTokensByVolume('24h');
// NEW: Get token overview with advanced filtering
// Backward compatible - simple limit still works
const simpleOverview = await client.getTokenOverview(50);
// NEW: Advanced filtering with markets and risk scoring
const filteredOverview = await client.getTokenOverview({
limit: 100,
minCurve: 60,
minHolders: 100,
reduceSpam: true,
minMarketCap: 100000,
markets: ['pumpfun', 'raydium', 'meteora'],
minRiskScore: 50,
maxRiskScore: 80,
});
// Returns: { latest: [...], graduating: [...], graduated: [...] }
// NEW: Get graduated tokens with filtering and pagination
const graduatedTokens = await client.getGraduatedTokens({
limit: 100,
page: 1,
reduceSpam: true,
minLiquidity: 50000,
markets: ['raydium'],
minMarketCap: 1000000,
});
// NEW: Get graduating tokens with filtering
const graduatingTokens = await client.getGraduatingTokens({
limit: 50,
minCurve: 70,
maxCurve: 95,
minHolders: 50,
maxHolders: 500,
minMarketCap: 50000,
markets: ['pumpfun', 'moonshot'],
});Token Overview Filtering Options
All token overview endpoints (getTokenOverview, getGraduatedTokens, getGraduatingTokens) support these filters:
interface TokenOverviewParams {
// Basic parameters
limit?: number; // Max results (default: 100, max: 500)
// Overview-specific
minCurve?: number; // Min curve percentage for graduating tokens
minHolders?: number; // Min number of holders for graduating
maxHolders?: number; // Max number of holders for graduating
reduceSpam?: boolean; // Filter out quick graduated tokens
// Graduated-specific
page?: number; // Page number for pagination
// Graduating-specific
minCurve?: number; // Min curve percentage (default: 40)
maxCurve?: number; // Max curve percentage (default: 100)
// Shared filters
minLiquidity?: number; // Minimum liquidity in USD
maxLiquidity?: number; // Maximum liquidity in USD
minMarketCap?: number; // Minimum market cap in USD
maxMarketCap?: number; // Maximum market cap in USD
markets?: string[]; // Array of markets: ['pumpfun', 'raydium', 'meteora', etc]
minRiskScore?: number; // Minimum risk score (0-100)
maxRiskScore?: number; // Maximum risk score (0-100)
rugged?: boolean; // Filter by rugged status
}Example: Find safe, high-liquidity graduated tokens
const safeTokens = await client.getGraduatedTokens({
limit: 50,
reduceSpam: true,
minLiquidity: 100000,
minMarketCap: 500000,
markets: ['raydium', 'meteora'],
maxRiskScore: 50,
rugged: false,
});Example: Monitor tokens about to graduate
const nearGraduation = await client.getGraduatingTokens({
minCurve: 85,
maxCurve: 99,
minHolders: 200,
minMarketCap: 100000,
markets: ['pumpfun'],
});Advanced Token Search (Enhanced!)
The searchTokens method now supports 60+ filter parameters for precise token discovery:
// Basic search with query
const basicSearch = await client.searchTokens({
query: 'TRUMP',
limit: 50,
sortBy: 'marketCapUsd',
sortOrder: 'desc',
});
// Search by exact symbol
const symbolSearch = await client.searchTokens({
symbol: 'TRUMP',
minLiquidity: 1000000,
});
// Filter by holder distribution - Find tokens with decentralized ownership
const decentralizedTokens = await client.searchTokens({
maxDev: 5, // Dev holds max 5%
maxTop10: 20, // Top 10 holders max 20%
maxInsiders: 10, // Insiders max 10%
maxSnipers: 5, // Snipers max 5%
minHolders: 1000, // At least 1000 holders
minLiquidity: 50000,
sortBy: 'holders',
sortOrder: 'desc',
});
// Find graduating Pump.fun tokens
const graduatingTokens = await client.searchTokens({
market: 'pumpfun',
minCurvePercentage: 80,
maxCurvePercentage: 99,
sortBy: 'curvePercentage',
sortOrder: 'desc',
});
// Search across multiple markets (NEW)
const multiMarketTokens = await client.searchTokens({
market: ['raydium', 'orca', 'pumpfun'],
minLiquidity: 50000,
sortBy: 'volume_24h',
sortOrder: 'desc',
});
// Filter by multiple launchpads (NEW)
const launchpadTokens = await client.searchTokens({
launchpad: ['pumpfun', 'boop'],
minMarketCap: 100000,
});
// Get full token objects from search (NEW)
const fullTokenSearch = await client.searchTokens({
query: 'TRUMP',
format: 'full',
limit: 50, // max 100 when format=full
});
// fullTokenSearch.data items include: token, pools, events, risk, buys, sells, txns, holders
// Search by social media presence
const tokensWithTwitter = await client.searchTokens({
twitter: 'https://x.com/realDonaldTrump',
minLiquidity: 10000,
});
// Find highly active tokens by fees
const activeTokens = await client.searchTokens({
minFeesTotal: 100, // At least 100 SOL in total fees
minFeesTrading: 50, // At least 50 SOL in trading fees
sortBy: 'fees.total',
sortOrder: 'desc',
limit: 20,
});
// Coin Communities — tokens with live community chat activity
const communityTokens = await client.searchTokens({
hasCoinCommunity: true,
sortBy: 'communityMessages',
sortOrder: 'desc',
limit: 50,
});
// At least 10 community messages and min $20k liquidity
const activeCommunityTokens = await client.searchTokens({
hasCoinCommunity: true,
minCommunityMessages: 10,
minLiquidity: 20000,
});
// Active pump.fun tokens with community chat
const pumpfunCommunity = await client.searchTokens({
market: 'pumpfun',
hasCoinCommunity: true,
sortBy: 'communityMessages',
sortOrder: 'desc',
});
// Each result includes hasCoinCommunity and communityMessages on the pool row
// Filter by volume across different timeframes
const highVolumeTokens = await client.searchTokens({
minVolume_24h: 100000, // At least $100k volume in 24h
minVolume_1h: 10000, // At least $10k volume in 1h
minLiquidity: 50000,
sortBy: 'volume_24h',
sortOrder: 'desc',
});
// Filter by transaction activity
const popularTokens = await client.searchTokens({
minBuys: 100,
minSells: 50,
minTotalTransactions: 150,
minHolders: 500,
sortBy: 'totalTransactions',
sortOrder: 'desc',
});
// Find tokens with specific token characteristics
const safeTokens = await client.searchTokens({
freezeAuthority: 'null', // No freeze authority
mintAuthority: 'null', // No mint authority
lpBurn: 100, // 100% LP burned
minLiquidity: 25000,
sortBy: 'liquidityUsd',
sortOrder: 'desc',
});
// Search by creator wallet
const creatorTokens = await client.searchTokens({
creator: 'CreatorWalletAddress',
sortBy: 'createdAt',
sortOrder: 'desc',
});
// Cursor-based pagination (faster for deep pagination)
let allResults = [];
let cursor = undefined;
do {
const results = await client.searchTokens({
minLiquidity: 5000,
limit: 100,
cursor: cursor,
});
allResults = allResults.concat(results.data);
cursor = results.nextCursor;
} while (cursor && results.hasMore);
console.log(`Found ${allResults.length} tokens`);
// Complex multi-filter search
const customSearch = await client.searchTokens({
// Liquidity and Market Cap
minLiquidity: 100000,
maxLiquidity: 10000000,
minMarketCap: 500000,
maxMarketCap: 50000000,
// Holder Distribution
minHolders: 500,
maxTop10: 30,
maxDev: 10,
maxInsiders: 15,
maxSnipers: 5,
// Volume (24h)
minVolume_24h: 50000,
// Transaction Activity
minBuys: 50,
minTotalTransactions: 100,
// Token Characteristics
freezeAuthority: 'null',
mintAuthority: 'null',
lpBurn: 100,
// Fees (indicates active trading)
minFeesTotal: 10,
// Sorting and Pagination
sortBy: 'volume_24h',
sortOrder: 'desc',
limit: 50,
showPriceChanges: true,
});Search Parameters Reference
Search & Pagination:
query- Search term for token symbol, name, or addresssymbol- Search for tokens with exact symbol matchpage- Page number for offset-based pagination (1-based)limit- Results per page (default: 100, max: 500)cursor- Cursor for cursor-based pagination (faster for deep searches)sortBy- Field to sort by (see sortable fields below)sortOrder- Sort order:'asc'or'desc'showAllPools- Return all pools for each token (default: false)showPriceChanges- Include price change data in response (default: false)
Sortable Fields:
- Market data:
liquidityUsd,marketCapUsd,priceUsd,volume,volume_5m,volume_15m,volume_30m,volume_1h,volume_6h,volume_12h,volume_24h - Holder distribution:
top10,dev,insiders,snipers,holders - Trading activity:
buys,sells,totalTransactions - Fees:
fees.total,fees.totalTrading,fees.totalTips - Other:
createdAt,lpBurn,curvePercentage
Creation Filters:
minCreatedAt- Minimum creation date (unix timestamp in ms)maxCreatedAt- Maximum creation date (unix timestamp in ms)
Liquidity & Market Cap Filters:
minLiquidity- Minimum liquidity in USDmaxLiquidity- Maximum liquidity in USD (capped at $100B)minMarketCap- Minimum market cap in USDmaxMarketCap- Maximum market cap in USD (capped at $5T)
Volume Filters:
minVolume/maxVolume- General volume filtersvolumeTimeframe- Timeframe for volume:'5m','15m','30m','1h','6h','12h','24h'- Timeframe-specific:
minVolume_5m,maxVolume_5m,minVolume_15m,maxVolume_15m,minVolume_30m,maxVolume_30m,minVolume_1h,maxVolume_1h,minVolume_6h,maxVolume_6h,minVolume_12h,maxVolume_12h,minVolume_24h,maxVolume_24h
Transaction Filters:
minBuys/maxBuys- Filter by number of buy transactionsminSells/maxSells- Filter by number of sell transactionsminTotalTransactions/maxTotalTransactions- Filter by total transactions
Holder Filters:
minHolders/maxHolders- Filter by total holder countminTop10/maxTop10- Filter by % held by top 10 holders (0-100)minDev/maxDev- Filter by % held by developer (0-100)minInsiders/maxInsiders- Filter by % held by insiders (0-100)minSnipers/maxSnipers- Filter by % held by snipers (0-100)
Token Characteristics:
lpBurn- LP token burn percentage (0-100)market- Market identifier or array of markets (e.g.,'pumpfun','raydium', or['raydium', 'orca', 'pumpfun'])freezeAuthority- Freeze authority address (use'null'for none)mintAuthority- Mint authority address (use'null'for none)deployer- Deployer wallet addresscreator- Token creator wallet addressstatus- Token status ('graduating','graduated','default')minCurvePercentage/maxCurvePercentage- Bonding curve % (0-100)
Social Media Filters (exact match):
twitter- Twitter/X profile URLtelegram- Telegram channel/group URLdiscord- Discord server invite URLwebsite- Official website URLfacebook- Facebook page URLinstagram- Instagram profile URLyoutube- YouTube channel URLreddit- Reddit community URLtiktok- TikTok profile URLgithub- GitHub repository URL
Launchpad Filter:
launchpad- Launchpad name or array of names (e.g.,'pumpfun', or['pumpfun', 'boop'])
Format:
format- Set to'full'to return full token objects (same shape as/tokens/:token). Whenformat=full, max limit is capped at 100.
Fees Filters (in SOL):
minFeesTotal/maxFeesTotal- Total fees paidminFeesTrading/maxFeesTrading- Trading fees paidminFeesTips/maxFeesTips- Priority fees/tips paid
Coin Communities Filters:
hasCoinCommunity-truefor tokens with at least one monitored community message;falsefor tokens with zero messagesminCommunityMessages/maxCommunityMessages- Filter by live message count range (inclusive)- Sort by
sortBy=communityMessageswithsortOrder=asc|desc
Search Response
The search response includes pagination information and detailed token data:
// Default response returns SearchResult items
// With format: 'full', returns TokenDetailResponse items
interface SearchResponse<T = SearchResult> {
status: string;
data: T[]; // Array of token results (SearchResult or TokenDetailResponse when format=full)
total?: number; // Total number of results
pages?: number; // Total number of pages
page?: number; // Current page number
nextCursor?: string; // Cursor for next page (cursor-based pagination)
hasMore?: boolean; // Whether there are more results
}
interface SearchResult {
// Basic token info
name: string;
symbol: string;
mint: string;
image?: string;
decimals: number;
// Pool and market info
poolAddress: string;
market: string;
quoteToken: string;
// Price and liquidity
liquidityUsd: number;
marketCapUsd: number;
priceUsd: number;
// Trading activity
buys: number;
sells: number;
totalTransactions: number;
volume: number;
volume_5m: number;
volume_15m: number;
volume_30m: number;
volume_1h: number;
volume_6h: number;
volume_12h: number;
volume_24h: number;
// Holder information
holders: number;
top10?: number; // % held by top 10 holders
dev?: number; // % held by developer
insiders?: number; // % held by insiders
snipers?: number; // % held by snipers
// Verification
jupiter?: boolean; // Jupiter verification status
verified?: boolean; // General verification status
// Token characteristics
lpBurn: number;
freezeAuthority: string | null;
mintAuthority: string | null;
deployer: string;
status: string;
createdAt: number;
lastUpdated: number;
// Social media
socials?: {
twitter?: string;
telegram?: string;
discord?: string;
website?: string;
facebook?: string;
instagram?: string;
youtube?: string;
reddit?: string;
tiktok?: string;
github?: string;
};
// Coin Communities (live chat message counts)
hasCoinCommunity?: boolean;
communityMessages?: number;
// Fees information
fees?: {
total?: number; // Total fees in SOL
totalTrading?: number; // Trading fees in SOL
totalTips?: number; // Priority fees/tips in SOL
};
// Token creation details
tokenDetails?: {
creator: string;
tx: string;
time: number;
};
}Price Endpoints
// Get token price
const tokenPrice = await client.getPrice('tokenAddress', true); // Include price changes
// Get historic price information
const priceHistory = await client.getPriceHistory('tokenAddress');
// Get price at a specific timestamp
const timestampPrice = await client.getPriceAtTimestamp(
'tokenAddress',
1690000000
);
// Get price range (lowest/highest in time range)
const priceRange = await client.getPriceRange(
'tokenAddress',
1690000000,
1695000000
);
// Get price using POST method
const postedPrice = await client.postPrice('tokenAddress');
// Get multiple token prices
const multiplePrices = await client.getMultiplePrices([
'So11111111111111111111111111111111111111112',
'4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
]);
// Get multiple token prices using POST
const postedMultiplePrices = await client.postMultiplePrices([
'So11111111111111111111111111111111111111112',
'4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R',
]);Wallet Endpoints
// Get basic wallet information
const walletBasic = await client.getWalletBasic('walletAddress');
// Get all tokens in a wallet
const wallet = await client.getWallet('walletAddress');
// Get wallet tokens with pagination
const walletPage = await client.getWalletPage('walletAddress', 2);
// Get wallet portfolio chart data with historical values and PnL
const walletChart = await client.getWalletChart('walletAddress');
console.log('24h PnL:', walletChart.pnl['24h']);
console.log('30d PnL:', walletChart.pnl['30d']);
console.log('Chart data points:', walletChart.chartData.length);
// Get wallet trades
const walletTrades = await client.getWalletTrades(
'walletAddress',
undefined,
true,
true,
false
);Trade Endpoints
// Get trades for a token
const tokenTrades = await client.getTokenTrades('tokenAddress');
// Get trades for a specific token and pool
const poolTrades = await client.getPoolTrades('tokenAddress', 'poolAddress');
// Get trades for a specific token, pool, and wallet
const userPoolTrades = await client.getUserPoolTrades(
'tokenAddress',
'poolAddress',
'walletAddress'
);
// Get trades for a specific token and wallet
const userTokenTrades = await client.getUserTokenTrades(
'tokenAddress',
'walletAddress'
);Chart Endpoints
// Get OHLCV data for a token - NEW: Now supports object syntax
// Method 1: Object syntax (recommended for multiple parameters)
const chartData = await client.getChartData({
tokenAddress: 'tokenAddress',
type: '1h',
timeFrom: 1690000000,
timeTo: 1695000000,
marketCap: false,
removeOutliers: true,
dynamicPools: true, // NEW: Dynamic pool selection
timezone: 'current', // NEW: Use current timezone or specify like 'America/New_York'
fastCache: true, // NEW: Enable fast cache for better performance
currency: 'usd', // NEW: Currency for price data - 'usd' (default), 'eur', or 'sol'
});
// Method 2: Traditional syntax (still supported)
const chartData = await client.getChartData(
'tokenAddress',
'1h',
1690000000,
1695000000,
false, // marketCap
true, // removeOutliers
true, // dynamicPools
'current', // timezone
true, // fastCache
'eur' // currency
);
// Get chart in different currencies
const chartInEur = await client.getChartData({
tokenAddress: 'tokenAddress',
type: '1h',
currency: 'eur',
});
const chartInSol = await client.getChartData({
tokenAddress: 'tokenAddress',
type: '1h',
currency: 'sol',
});
// Get OHLCV data for a specific token and pool
const poolChartData = await client.getPoolChartData({
tokenAddress: 'tokenAddress',
poolAddress: 'poolAddress',
type: '15m',
timezone: 'UTC',
fastCache: false,
currency: 'usd', // Also supports currency option
});
// Get holder count chart data
const holdersChart = await client.getHoldersChart('tokenAddress', '1d');
// Get snipers percentage chart data
const snipersChart = await client.getSnipersChart('tokenAddress', '1d');
// Get insiders percentage chart data
const insidersChart = await client.getInsidersChart('tokenAddress', '1d');
// Get bundlers percentage chart data
const bundlersChart = await client.getBundlersChart('tokenAddress', '1d');PnL Endpoints
// Get PnL data for all positions of a wallet
const walletPnL = await client.getWalletPnL('walletAddress', true, true, false);
// Get the first 100 buyers of a token with PnL data
const firstBuyers = await client.getFirstBuyers('tokenAddress');
// Get PnL data for a specific token in a wallet - NEW: holdingCheck parameter
const tokenPnL = await client.getTokenPnL(
'walletAddress',
'tokenAddress',
true
);
// Can also use object syntax
const tokenPnL = await client.getTokenPnL({
wallet: 'walletAddress',
tokenAddress: 'tokenAddress',
holdingCheck: true,
});PnL v2 REST (/v2/pnl)
PnL v2 adds typed methods on Client for leaderboards, per-token traders, wallet analytics, and batch position lookups. Types are exported from the package with the PnlV2 prefix (for example PnlV2WalletOverviewResponse, PnlV2KOLLeaderboardParams).
Token scope: PnL v2 targets meme and tradable SPL positions (for example pump-style tokens). It is not for tracking SOL (native or wrapped So111...).
Wallet endpoints may return a queued payload while the wallet is being indexed: check for queued === true and indexed === false (see PnlV2WalletQueued).
pnlMode parameter
Endpoints that accept pnlMode control how positions flagged by the invalid-PnL heuristic are counted toward realized PnL:
| Mode | Behaviour |
|------|-----------|
| strict (default) | Flagged positions contribute 0 |
| adjusted | Flagged positions get a cost-basis-capped value |
| raw | No adjustment — realized_pnl passed through as-is |
Accepted on: top traders (/v2/pnl/leaderboard/top), wallet overview (/v2/pnl/wallets/:wallet), wallet positions, wallet-token position, and all three position batch endpoints. The server also accepts the aliases pnl_mode and mode.
import type { PnlMode } from '@solana-tracker/data-api';
const mode: PnlMode = 'adjusted';
const top = await client.getPnlV2TopTraders({ days: 90, pnlMode: mode, limit: 50 });
// top.pagination.pnlMode → 'adjusted'
// top.traders[0].pnlAdjustments → { mode, invalidPnl, adjustedCorrection }
// top.traders[0].period.realizedRaw → gross PnL before filtering
// top.traders[0].period.days → { profitable, losing, maxSinglePnl, winRate }Unified identity
Every wallet-scoped response now carries an identity field (top-level or per-row). It can include multiple tags and pluggable sources (kol, bot, pool, developer, hacker, spam_dusting, exchange, SNS, platform tags, arbitrage, etc.):
import type { PnlV2Identity } from '@solana-tracker/data-api';
// identity?: { name, twitter, avatar, type, tags, platforms, sns, bot, pool, developer, hacker, spamDusting, exchange }
// SNS-only wallet — name is the primary .sol domain
// { name: 'solanatracker.sol', sns: { domain: 'solanatracker.sol' }, tags: ['sns'], type: 'sns' }
// KOL + SNS — higher-priority label keeps name; domain still on sns
// { name: 'SomeKolName', sns: { domain: 'solanatracker.sol' }, tags: ['kol', 'sns'], type: 'kol' }Token-scoped PnL enrichment
Token-scoped endpoints (/tokens/:token/traders, first-buyers, token batch) return PnL nested as pnl: { token, wallet } so each row makes it unambiguous which scope each PnL belongs to. Identity enrichment (pool, developer, platforms) is always on.
const traders = await client.getPnlV2TokenTraders('mint', { limit: 10 });
// traders.traders[0].pnl.token → { realized, unrealized, total }
// traders.traders[0].pnl.wallet → { realized, unrealized, total, invested, proceeds, totalTrades, tokensTraded }
// traders.traders[0].identity → pool / developer / platform tagsToken holders enrichment
getTokenHolders now accepts an optional enrich parameter to add identity and/or wallet PnL:
const holders = await client.getTokenHolders('mint', 'all');
// holders.enrich → ['identity', 'walletPnl']
// holders.accounts[0].identity → pool / developer tags
// holders.accounts[0].pnl?.wallet → lifetime wallet PnL
// holders.accounts[0].pnl?.token → per-token PnLQuick reference
// Leaderboards
const kols = await client.getPnlV2KOLLeaderboard({ sort: 'total', direction: 'desc', limit: 20 });
const kolsPeriod = await client.getPnlV2KOLPeriodLeaderboard({ period: '30d', limit: 20 });
const cal = await client.getPnlV2KOLCalendar({ year: 2026, month: 4 });
const day = await client.getPnlV2KOLByDate({ date: '2026-04-01' });
const top = await client.getPnlV2TopTraders({ days: 90, limit: 50, pnlMode: 'adjusted' });
// Token (enriched with identity + wallet PnL automatically)
const traders = await client.getPnlV2TokenTraders('tokenMint', { limit: 50, sort: 'pnl', direction: 'desc' });
const firstBuyers = await client.getPnlV2TokenFirstBuyers('tokenMint', { sort: 'first_trade', direction: 'asc' });
// Wallet (narrow with queued guard when needed)
const overview = await client.getPnlV2WalletOverview('walletAddress', { pnlMode: 'adjusted' });
if ('queued' in overview && overview.queued) {
console.log(overview.message);
} else {
console.log(overview.summary.roi, overview.identity, overview.pnlMode);
}
const history = await client.getPnlV2WalletHistory('walletAddress', { period: '30d', limit: 30 });
const performance = await client.getPnlV2WalletPerformance('walletAddress', { period: '90d' });
const onePosition = await client.getPnlV2WalletTokenPosition('walletAddress', 'tokenMint', { pnlMode: 'adjusted' });
const highlights = await client.getPnlV2WalletHighlights('walletAddress');
const risk = await client.getPnlV2WalletRisk('walletAddress');
const positions = await client.getPnlV2WalletPositions('walletAddress', { filter: 'holding', limit: 50, pnlMode: 'adjusted' });
const chart = await client.getPnlV2WalletChart('walletAddress', { time_from: startUnix, time_to: endUnix });
// Batch (limits: 100 wallet summaries, 100 tokens per wallet batch, 200 wallets per token batch, 200 pairs)
await client.batchPnlV2WalletSummaries(['walletA', 'walletB']); // wallet-level summary + identity + raw tags
await client.batchPnlV2WalletTokenPositions('walletAddress', ['mintA', 'mintB'], { pnlMode: 'adjusted' });
await client.batchPnlV2TokenWalletPositions('tokenMint', ['walletA', 'walletB'], { pnlMode: 'adjusted' });
await client.batchPnlV2PositionPairs([{ wallet: 'walletA', token: 'mintA' }], { pnlMode: 'raw' });
// Token holders with enrichment (not PnL v2 — different endpoint)
const holders = await client.getTokenHolders('tokenMint', 'all');See examples/pnl-v2.ts for runnable snippets.
Jupiter DCA REST (/dca/*)
Track Jupiter recurring (DCA) orders. All endpoints accept an optional program query param (default "jupiter"); aliases dex and platform are also supported server-side.
| Method | Description |
|--------|-------------|
| getDcaPrograms() | List supported DCA programs (id, label, programId) |
| getDcaWallet(wallet, params?) | Wallet's DCA orders + status summary (active/paused/completed/pending) |
| getDcaWalletOrders(wallet, params?) | Paginated wallet orders (no summary) |
| getDcaOrder(address, params?) | Single DCA account by pubkey |
| getDcaTokenFlow(mint, params?) | Aggregate buyer/seller flow for a token |
| getDcaTokenBuyers(mint, params?) | Orders buying the token (output mint) |
| getDcaTokenSellers(mint, params?) | Orders selling the token (input mint) |
| getDcaTokenUsers(mint, params?) | Top wallets by DCA activity on a token |
| getDcaPair(inputMint, outputMint, params?) | Orders for a specific input → output pair |
Common list params:
limit— wallet/pair: 1–100 (default 50); token endpoints: 1–1000 (default 200)cursor— opaque pagination cursor (lastaddressfrom previous page)sort/sortBy—volume(default) |deposited|remaining|progress|recent|created|statusstatus—active|paused|completed|pending|all(defaultall)
// Wallet orders with status summary
const wallet = await client.getDcaWallet('walletAddress', {
status: 'active',
sort: 'recent',
limit: 25,
});
console.log(`${wallet.summary.active} active / ${wallet.summary.completed} completed`);
// Top buyers of a token (token = output mint)
const buyers = await client.getDcaTokenBuyers('tokenMint', { limit: 50 });
// All orders for a USDC → SOL pair
const pair = await client.getDcaPair(
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
'So11111111111111111111111111111111111111112', // SOL
{ sort: 'volume' },
);Each DcaOrder includes program info, status (active | paused | completed | pending), direction (buying | selling | mixed), the input/output DcaToken (with USD price when available), human-readable amounts (deposited, used, remaining, received, perCycle, volume) plus their *Usd siblings, frequency, progressPercent, createdAt/nextCycleAt ISO timestamps, and a raw map of base-units strings.
Jupiter DCA Datastream (dca:jupiter:*)
Subscribe to live DCA events on Premium, Business, and Enterprise plans. Each event is delivered to the global room (dca:jupiter), the matching event-type room, and any scoped rooms that apply.
// All Jupiter DCA events
ds.subscribe.dca.all().on((ev) => { /* … */ });
// Cycle fills only
ds.subscribe.dca.filled().on((ev) => {
console.log(`fill ${ev.address}: $${ev.usd?.inAmountUsd} → $${ev.usd?.outAmountUsd}`);
});
// Other event-type rooms
ds.subscribe.dca.opened();
ds.subscribe.dca.closed();
ds.subscribe.dca.deposit();
ds.subscribe.dca.withdraw();
ds.subscribe.dca.collectedFee();
ds.subscribe.dca.position(); // throttled account snapshots, no signature
// Scoped rooms
ds.subscribe.dca.token('tokenMint').buyers();
ds.subscribe.dca.token('tokenMint').sellers();
ds.subscribe.dca.wallet('walletAddress');
ds.subscribe.dca.order('dcaAccountAddress');Transaction events (Opened, Filled, Closed, Deposit, Withdraw, CollectedFee) carry eventName, signature, on-chain raw amounts as strings, a usd block, optional prices map, and a snapshot order: DcaOrder. Position snapshots have no eventName or signature; they include writeVersion and a full order: DcaOrder.
import type { DcaStreamEvent, DcaPositionEvent } from '@solana-tracker/data-api';
function isPositionSnapshot(ev: DcaStreamEvent): ev is DcaPositionEvent {
return 'writeVersion' in ev;
}See examples/dca.ts and examples/dca-datastream.ts for runnable snippets.
Top Traders Endpoints
// Get the most profitable traders across all tokens
const topTraders = await client.getTopTraders(1, true, 'total');
// Get top 100 traders by PnL for a token
const tokenTopTraders = await client.getTokenTopTraders('tokenAddress');Events Endpoints (Live Data)
// Get raw event data for live processing
// NOTE: For non-live statistics, use getTokenStats() instead which is more efficient
const events = await client.getEvents('tokenAddress');
console.log('Total events:', events.length);
// Get events for a specific pool
const poolEvents = await client.getPoolEvents('tokenAddress', 'poolAddress');
// Process events into statistics using the processEvents utility
import { processEventsAsync } from '@solana-tracker/data-api';
const stats = await processEvents(events);
console.log('1h stats:', stats['1h']);
console.log('24h volume:', stats['24h']?.volume.total);Additional Endpoints
// Get detailed stats for a token
const tokenStats = await client.getTokenStats('tokenAddress');
// Get detailed stats for a specific token and pool
const poolStats = await client.getPoolStats('tokenAddress', 'poolAddress');
// Get remaining API credits
const credits = await client.getCredits();
console.log('Remaining credits:', credits.credits);
// Get subscription information
const subscription = await client.getSubscription();
console.log('Plan:', subscription.plan);
console.log('Credits:', subscription.credits);
console.log('Status:', subscription.status);
console.log('Next billing date:', subscription.next_billing_date);Error Handling
The library includes specific error types for robust error handling with enhanced error details:
import {
Client,
DataApiError,
RateLimitError,
ValidationError,
} from '@solana-tracker/data-api';
try {
const tokenInfo = await client.getTokenInfo('invalid-address');
} catch (error) {
if (error instanceof RateLimitError) {
console.error(
'Rate limit exceeded. Retry after:',
error.retryAfter,
'seconds'
);
} else if (error instanceof ValidationError) {
console.error('Validation error:', error.message);
} else if (error instanceof DataApiError) {
console.error('API error:', error.message, 'Status:', error.status);
// Access detailed error information
if (error.details) {
console.error('Error details:', error.details);
}
} else {
console.error('Unexpected error:', error);
}
}Type Definitions
Type Updates:
// Enhanced SearchResult with holder distribution
interface SearchResult {
// ... existing fields
top10?: number; // % held by top 10 holders
dev?: number; // % held by developer
insiders?: number; // % held by insiders
snipers?: number; // % held by snipers
jupiter?: boolean; // Jupiter verification
verified?: boolean; // General verification
socials?: {
twitter?: string;
telegram?: string;
discord?: string;
website?: string;
// ... more social media links
};
// Coin Communities
hasCoinCommunity?: boolean;
communityMessages?: number;
fees?: {
total?: number; // Total fees in SOL
totalTrading?: number; // Trading fees in SOL
totalTips?: number; // Priority tips in SOL
};
}
// Enhanced SearchResponse with pagination (generic - defaults to SearchResult, TokenDetailResponse when format=full)
interface SearchResponse<T = SearchResult> {
status: string;
data: T[];
total?: number;
pages?: number;
page?: number;
nextCursor?: string; // For cursor-based pagination
hasMore?: boolean;
}
// Deployer tokens response (generic - defaults to DeployerToken, TokenDetailResponse when format=full)
interface DeployerTokensResponse<T = DeployerToken> {
total: number;
tokens: T[];
}
// Deployer endpoint parameters
interface DeployerParams {
page?: number;
limit?: number; // max 500, max 100 when format=full
market?: string | string[]; // Single or multiple markets
launchpad?: string | string[]; // Single or multiple launchpads
format?: 'full'; // Return full token objects
}
// Paginated token holders response
interface PaginatedTokenHoldersResponse {
total: number;
accounts: PaginatedHolder[];
cursor: string;
hasMore: boolean;
limit: number;
}
interface PaginatedHolder {
wallet: string;
account: string;
amount: number;
value: {
quote: number;
usd: number;
};
percentage: number;
}
// Aggregated price update structure
interface AggregatedPriceUpdate {
token: string;
timestamp: number;
price: number;
pool: string;
aggregated: {
median: number;
average: number;
min: number;
max: number;
poolCount: number;
};
topPools: Array<{
poolId: string;
price: number;
liquidity: number;
market: string;
}>;
}
// Developer holding update structure
interface DevHoldingUpdate {
token: string;
creator: string;
amount: string;
percentage: number;
previousPercentage: number;
timestamp: number;
}
// Top holder information
interface TopHolder {
address: string;
amount: string;
percentage: number;
}
// Top 10 holders update structure
interface Top10HoldersUpdate {
token: string;
holders: TopHolder[];
totalPercentage: number;
previousPercentage: number | null;
timestamp: number;
}
// Snipers chart data
interface SnipersChartData {
percentage: number;
time: number;
}
interface SnipersChartResponse {
snipers: SnipersChartData[];
}
// Insiders chart data
interface InsidersChartData {
percentage: number;
time: number;
}
interface InsidersChartResponse {
insiders: InsidersChartData[];
}
// Bundlers chart data
interface BundlersChartData {
percentage: number;
time: number;
}
interface BundlersChartResponse {
bundlers: BundlersChartData[];
}WebSocket Data Stream Events
The Datastream extends the standard EventEmitter interface, allowing you to listen for various events:
// Connection events
dataStream.on('connected', () => console.log('Connected to WebSocket server'));
dataStream.on('disconnected', (socketType) =>
console.log(`Disconnected: ${socketType}`)
);
dataStream.on('reconnecting', (attempt) =>
console.log(`Reconnecting: attempt ${attempt}`)
);
dataStream.on('error', (error) => console.error('Error:', error));
// Data events
dataStream.on('latest', (data) => console.log('New token:', data));
dataStream.on(`price:aggregated:${tokenAddress}`, (data) =>
console.log('Aggregated price update:', data)
); // NEW - RECOMMENDED
dataStream.on(`price-by-token:${tokenAddress}`, (data) =>
console.log('Price update (deprecated - auto-switches to new main pool):', data)
);
dataStream.on(`price:${tokenAddress}`, (data) =>
console.log('Price update:', data)
);
dataStream.on(`price:${poolAddress}`, (data) =>
console.log('Price update:', data)
);
dataStream.on(`transaction:${tokenAddress}`, (data) =>
console.log('New transaction:', data)
);
dataStream.on(`wallet:${walletAddress}`, (data) =>
console.log('Wallet transaction:', data)
);
dataStream.on(`wallet:${walletAddress}:balance`, (data) =>
console.log('Wallet balance update:', data)
);
dataStream.on(`wallet:${walletAddress}:${tokenAddress}:balance`, (data) =>
console.log('Token balance update:', data)
);
dataStream.on('graduating', (data) => console.log('Graduating token:', d