UNPKG

@solana-tracker/data-api

Version:

Official Solana Tracker Data API client for accessing Solana Data

1,419 lines (1,160 loc) 67.8 kB
# Solana Tracker - Data API SDK Official JavaScript/TypeScript client for the [Solana Tracker Data API](https://www.solanatracker.io/data-api). [![npm version](https://badge.fury.io/js/@solana-tracker%2Fdata-api.svg)](https://badge.fury.io/js/@solana-tracker%2Fdata-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 rooms `pnl:{wallet}`, `pnl:{wallet}:{token}`, `pnl:{wallet}:summary` — scoped to meme / tradable tokens, not SOL. Includes `pnlMode` (strict/adjusted/raw), unified wallet identity (KOL, bot, pool, developer, hacker, spam-dusting, exchange, SNS primary `.sol` domain, platform tags), always-on token enrichment, opt-in holder enrichment, full position rows on `wallet/positions` and `tokens/:mint/traders`, and `summary.timing.avgHoldTimeSecs` on the wallet overview - **Jupiter DCA**: REST endpoints under `/dca/*` (programs, wallet orders, single order, token flow/buyers/sellers/users, trading pair) and Datastream rooms `dca: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: ```bash npm install @solana-tracker/data-api ``` Or with yarn: ```bash yarn add @solana-tracker/data-api ``` ## Quick Start ```typescript 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/`](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`](examples/tokens.ts) | Token info, holders, trending, search | | [`examples/prices.ts`](examples/prices.ts) | Price and multi-price endpoints | | [`examples/wallets.ts`](examples/wallets.ts) | Wallet balances and portfolio | | [`examples/trades.ts`](examples/trades.ts) | Token and pool trades | | [`examples/charts.ts`](examples/charts.ts) | OHLCV and holder/snipers charts | | [`examples/stats.ts`](examples/stats.ts) | Token and pool stats | | [`examples/bundlers.ts`](examples/bundlers.ts) | Bundler-related API usage | | [`examples/profit-loss.ts`](examples/profit-loss.ts) | **Legacy** PnL endpoints (`getWalletPnL`, `getTokenPnL`, first buyers, top traders) | | [`examples/pnl-v2.ts`](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`](examples/pnl-v2-datastream.ts) | **PnL v2 Datastream**: `subscribe.pnl.position`, `.wallet`, `.summary` with `tradeUpdate`, `balanceUpdate`, `priceUpdate`, and wallet summary payloads | | [`examples/dca.ts`](examples/dca.ts) | **Jupiter DCA REST**: `getDcaPrograms`, wallet/orders, single order, token flow/buyers/sellers/users, pair | | [`examples/dca-datastream.ts`](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`](examples/datastream.ts) | WebSocket subscriptions (prices, txs, stats, volume, etc.) | | [`examples/utils.ts`](examples/utils.ts) | Shared helpers (`handleError`, formatting) | ## What's New ### Version Updates #### Latest Features: 1. **Multiple Markets & Launchpads**: `market` and `launchpad` now accept comma-separated values or arrays on both `/search` and `/deployer`: ```typescript // 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', }); ``` 2. **Full Token Format**: `format=full` returns results as full token objects (same shape as `/tokens/:token`) on both `/search` and `/deployer`: ```typescript // 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, }); ``` 3. **Deployer Endpoint Enhancements**: The `/deployer` endpoint now supports `launchpad`, `market`, and `format` filters via a params object (backward compatible with positional args). 4. **Pool Creation Data**: New and graduated pool messages now include `creation` data (creator, created_tx, created_time) inside the pool object. 5. **Top Performers Endpoint**: Get the best performing tokens launched today: ```typescript 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)); ``` 2. **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 2. **Paginated Token Holders**: - Efficient pagination through large holder lists - Support for up to 5000 holders per request - Cursor-based navigation for seamless data retrieval 3. **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 4. **Smart Primary Pool Routing**: - Automatic switching to new primary pools when liquidity migrates - Works for both `token:primary` and `price-by-token` subscriptions - No code changes needed - happens automatically 5. **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 6. **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()` methods 7. **Total Stats Rooms**: Subscribe to direct total stats objects with `.stats.total.token()` and `.stats.total.pool()` 8. **Volume Rooms**: Subscribe to high-frequency USD volume aggregation via `.volume.pool()` and `.volume.token()` 9. **Developer Holdings Tracking**: Monitor developer/creator wallet holdings in real-time with `.dev.holding()` method 10. **Top 10 Holders Monitoring**: Track the top 10 holders and their combined percentage of token supply with `.top10()` method 11. **Global 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: ```typescript 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: ```typescript // 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 wallets ``` Each subscription method returns a response object with: - `room`: The subscription channel name - `on()`: Method to attach a listener with proper TypeScript types - Returns an object with `unsubscribe()` method for easy cleanup ### Smart Primary Pool Routing The `token:primary` and `price-by-token` subscriptions now automatically switch to the new primary pool when liquidity migrates: ```typescript // 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: ```typescript // 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 transactions ``` ### Migration Guide for Price Updates The `price.token()` method is deprecated in favor of `price.aggregated()`: ```typescript // 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: ```typescript // 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 ```typescript // 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: ```typescript 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** ```typescript 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** ```typescript 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: ```typescript // 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 address - `symbol` - Search for tokens with exact symbol match - `page` - 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 USD - `maxLiquidity` - Maximum liquidity in USD (capped at $100B) - `minMarketCap` - Minimum market cap in USD - `maxMarketCap` - Maximum market cap in USD (capped at $5T) **Volume Filters:** - `minVolume` / `maxVolume` - General volume filters - `volumeTimeframe` - 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 transactions - `minSells` / `maxSells` - Filter by number of sell transactions - `minTotalTransactions` / `maxTotalTransactions` - Filter by total transactions **Holder Filters:** - `minHolders` / `maxHolders` - Filter by total holder count - `minTop10` / `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 address - `creator` - Token creator wallet address - `status` - Token status (`'graduating'`, `'graduated'`, `'default'`) - `minCurvePercentage` / `maxCurvePercentage` - Bonding curve % (0-100) **Social Media Filters (exact match):** - `twitter` - Twitter/X profile URL - `telegram` - Telegram channel/group URL - `discord` - Discord server invite URL - `website` - Official website URL - `facebook` - Facebook page URL - `instagram` - Instagram profile URL - `youtube` - YouTube channel URL - `reddit` - Reddit community URL - `tiktok` - TikTok profile URL - `github` - 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`). When `format=full`, max limit is capped at 100. **Fees Filters (in SOL):** - `minFeesTotal` / `maxFeesTotal` - Total fees paid - `minFeesTrading` / `maxFeesTrading` - Trading fees paid - `minFeesTips` / `maxFeesTips` - Priority fees/tips paid **Coin Communities Filters:** - `hasCoinCommunity` - `true` for tokens with at least one monitored community message; `false` for tokens with zero messages - `minCommunityMessages` / `maxCommunityMessages` - Filter by live message count range (inclusive) - Sort by `sortBy=communityMessages` with `sortOrder=asc|desc` #### Search Response The search response includes pagination information and detailed token data: ```typescript // 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 ```typescript // 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 ```typescript // 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 ```typescript // 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 ```typescript // 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 ```typescript // 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`. ```typescript 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, `arbitr