Node.js/TypeScript SDK and CLI for consuming real-time WebSocket streams from the DexScreener Realtime Monitor Apify Actor. This client makes it easy to integrate real-time DexScreener data into your backends, bots, and data pipelines with flexible authentication and automatic reconnection.
- User Guide - Complete tutorials, examples, and best practices
- API Reference - Technical reference for all classes and methods
- Examples - Working code examples
- Node.js 18.0.0 or higher
- Apify API Token (get one here)
- Clone the repository:
git clone https://github.com/muhammetakkurtt/dexscreener-realtime-client.git
cd dexscreener-realtime-client- Install dependencies:
npm install- Build the project:
npm run build- Configure environment variables:
cp .env.example .env
# Edit .env with your credentials- Run an example:
npx tsx examples/basic-sdk.tsCreate a .env file in the project root with the following variables:
| Variable | Description |
|---|---|
APIFY_TOKEN |
Your Apify API token for authentication |
DEX_ACTOR_BASE |
Base URL for your DexScreener Realtime Monitor Actor (supports http/https/ws/wss protocols) |
Example:
APIFY_TOKEN=apify_api_xxxxxxxxxxxxx
DEX_ACTOR_BASE=https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actorProtocol Normalization: The SDK automatically converts HTTP/HTTPS URLs to WebSocket (WS/WSS) protocols:
http://βws://https://βwss://ws://andwss://are used as-is
You can provide the base URL in any format - the SDK handles the conversion automatically.
The simplest way to get started with default settings:
import { DexScreenerStream } from './dist/index.js';
const stream = new DexScreenerStream({
baseUrl: process.env.DEX_ACTOR_BASE!,
apiToken: process.env.APIFY_TOKEN!,
pageUrl: 'https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc',
onPair: (pair) => {
console.log(`${pair.baseToken?.symbol}: $${pair.priceUsd}`);
},
});
stream.start();The SDK supports flexible authentication strategies:
import { DexScreenerStream } from './dist/index.js';
const stream = new DexScreenerStream({
baseUrl: process.env.DEX_ACTOR_BASE!,
apiToken: process.env.APIFY_TOKEN!,
pageUrl: 'https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc',
streamId: 'solana-trending',
authMode: 'auto', // 'auto' | 'header' | 'query' | 'both' (default: 'auto')
onBatch: (event, { streamId }) => {
console.log(`[${streamId}] Received ${event.pairs?.length ?? 0} pairs`);
},
onPair: (pair, { streamId }) => {
console.log(`[${streamId}] ${pair.baseToken?.symbol}/${pair.quoteToken?.symbol} ${pair.priceUsd}`);
},
onError: (error, { streamId }) => {
console.error(`[${streamId}] Error:`, error);
},
onStateChange: (state, { streamId }) => {
console.log(`[${streamId}] State: ${state}`);
},
});
stream.start();
// Stop when done
// stream.stop();Authentication Modes:
auto(default): Tries header authentication first, falls back to query parameter if neededheader: Sends token only in Authorization header (more secure)query: Sends token only as URL query parameterboth: Sends token in both header and query parameter
Monitor multiple DexScreener pages simultaneously:
import { DexScreenerMultiStream } from './dist/index.js';
const multi = new DexScreenerMultiStream({
baseUrl: process.env.DEX_ACTOR_BASE!,
apiToken: process.env.APIFY_TOKEN!,
authMode: 'auto', // Applied to all streams
streams: [
{ id: 'solana-trending', pageUrl: 'https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc' },
{ id: 'base-latest', pageUrl: 'https://dexscreener.com/base?rankBy=trendingScoreH6&order=desc&minLiq=30000' },
{ id: 'ethereum-volume', pageUrl: 'https://dexscreener.com/ethereum?rankBy=volume&order=desc' },
],
onPair: (pair, { streamId }) => {
console.log(`[${streamId}] ${pair.baseToken?.symbol} ${pair.priceUsd}`);
},
});
multi.startAll();
// Stop all streams
// multi.stopAll();See examples/ directory for complete working examples.
After building the project, use the CLI to consume streams without writing code.
Print JSON events to stdout:
node dist/cli.cjs \
--base-url https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor \
--api-token apify_api_xxxxxxxxxxxxx \
--page-url "https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc" \
--auth-mode autoOr using environment variable:
export APIFY_TOKEN=apify_api_xxxxxxxxxxxxx
node dist/cli.cjs \
--base-url https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor \
--page-url "https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc"Append events to a JSON Lines file:
node dist/cli.cjs \
--base-url https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor \
--page-url "https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc" \
--mode jsonl \
--jsonl-path ./events.jsonlForward events to an HTTP endpoint:
node dist/cli.cjs \
--base-url https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor \
--page-url "https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc" \
--mode webhook \
--webhook-url https://api.example.com/webhooks/dexscreenernode dist/cli.cjs \
--base-url https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor \
--page-url "https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc" \
--page-url "https://dexscreener.com/base?rankBy=trendingScoreH6&order=desc" \
--page-url "https://dexscreener.com/ethereum?rankBy=trendingScoreH6&order=desc"| Option | Description | Required |
|---|---|---|
--base-url |
Apify Standby Actor base URL (http/https/ws/wss) | Yes |
--api-token |
Apify API token (or use APIFY_TOKEN env) |
Yes |
--page-url |
DexScreener page URL(s) to monitor | Yes |
--auth-mode |
Authentication mode: auto, header, query, both |
No (default: auto) |
--mode |
Output mode: stdout, jsonl, webhook |
No (default: stdout) |
--jsonl-path |
File path for JSONL output | Required for jsonl mode |
--webhook-url |
Webhook URL for HTTP POST | Required for webhook mode |
--retry-ms |
Reconnection delay in ms | No (default: 3000) |
--keep-alive-ms |
Health check interval in ms | No (default: 120000, set to 0 to disable) |
type DexStreamOptions = {
baseUrl: string; // Apify Standby Actor base URL (http/https/ws/wss)
apiToken: string; // Apify API token
pageUrl: string; // DexScreener page URL
streamId?: string; // Optional stream identifier
authMode?: 'auto' | 'header' | 'query' | 'both'; // Authentication mode (default: 'auto')
retryMs?: number; // Reconnection delay (default: 3000)
keepAliveMs?: number; // Health check interval (default: 120000, set to 0 to disable)
onBatch?: (event: DexEvent, ctx: StreamContext) => void;
onPair?: (pair: Pair, ctx: StreamContext) => void;
onError?: (error: unknown, ctx: StreamContext) => void;
onStateChange?: (state: ConnectionState, ctx: StreamContext) => void;
};type MultiStreamConfig = {
baseUrl: string;
apiToken: string;
streams: Array<{ id: string; pageUrl: string }>;
authMode?: 'auto' | 'header' | 'query' | 'both'; // Authentication mode for all streams (default: 'auto')
retryMs?: number;
keepAliveMs?: number;
onBatch?: (event: DexEvent, ctx: StreamContext) => void;
onPair?: (pair: Pair, ctx: StreamContext) => void;
onError?: (error: unknown, ctx: StreamContext) => void;
onStateChange?: (state: ConnectionState, ctx: StreamContext) => void;
};type DexEvent = {
stats?: DexEventStats;
pairs?: Pair[];
event_type?: string;
timestamp?: string;
};type Pair = {
// Identity & Classification
type?: PairType; // Raw pair type discriminator (e.g. typeAMM)
chainId?: string; // Blockchain network (e.g., "solana", "ethereum")
dexId?: string; // DEX identifier (e.g., "raydium", "uniswap")
pairAddress?: string; // Unique pair contract address
labels?: string[]; // Classification labels (e.g., ["CPMM"])
// Token Information
baseToken?: Token; // Base token details
quoteToken?: Token; // Quote token details
quoteTokenSymbol?: string; // Quote token symbol (convenience field)
// Pricing
price?: string; // Price in quote token
priceUsd?: string; // Normalized USD price
priceUSD?: string; // Raw USD price from DexScreener, when present
priceChange?: PriceChange; // Price changes over time periods
// Trading Activity
txns?: Txns; // Transaction counts (buys/sells)
buyers?: UserCounts; // Unique buyer counts over time periods (m5, h1, h6, h24)
sellers?: UserCounts; // Unique seller counts over time periods (m5, h1, h6, h24)
makers?: UserCounts; // Unique maker counts over time periods (m5, h1, h6, h24)
// Volume Metrics
volume?: Volume; // Total trading volume over time periods
volumeBuy?: Volume; // Buy-side volume over time periods
volumeSell?: Volume; // Sell-side volume over time periods
// Market Data
liquidity?: Liquidity; // Liquidity in USD, base, and quote tokens
marketCap?: number; // Market capitalization
fdv?: number; // Fully diluted valuation
// Metadata & Features
pairCreatedAt?: number; // Normalized Unix timestamp in milliseconds
pairCreatedAtRaw?: PairCreatedAtRaw; // Raw { seconds, nanos } timestamp, when present
profile?: Profile; // Token profile information (eti, header, website, twitter, linkCount, imgKey)
cmsProfile?: CmsProfile; // CMS profile information (headerId, iconId, description, links, nsfw)
isBoostable?: boolean; // Whether pair can be boosted
boosts?: Boosts; // Active boost information
launchpad?: Launchpad; // Launchpad information (progress, creator, migrationDex, meta)
// Additional Fields
c?: string; // Additional metadata field
a?: string; // Additional metadata field
// Future Extensibility
[key: string]: unknown; // Allows access to new fields added by the API
};The SDK normalizes current Actor payloads before invoking callbacks. Raw fields such as priceUSD, stats.*.volumeUSD, pairCreatedAt: { seconds, nanos }, and nested type.value.launchpad are preserved where useful, while camelCase aliases like priceUsd, stats.*.volumeUsd, pairCreatedAt, quoteTokenSymbol, and launchpad.migrationDex are guaranteed for SDK consumers.
type PairCreatedAtRaw = {
seconds?: number | string;
nanos?: number | string;
};
type PairType = {
case?: string;
value?: {
a?: string;
launchpad?: Launchpad;
[key: string]: unknown;
};
};The SDK automatically parses WebSocket messages and delivers clean DexEvent objects to your callbacks. Here's what you receive in the onBatch callback:
{
"stats": {
"m5": { "txns": 54923, "volumeUsd": 9045447.56, "volumeUSD": 9045447.56 },
"h1": { "txns": 756730, "volumeUsd": 134179114.12, "volumeUSD": 134179114.12 },
"h6": { "txns": 4880021, "volumeUsd": 1151490222.86, "volumeUSD": 1151490222.86 },
"h24": { "txns": 19323940, "volumeUsd": 4377335189.87, "volumeUSD": 4377335189.87 }
},
"pairs": [
{
"chainId": "solana",
"dexId": "raydium",
"labels": ["CPMM"],
"pairAddress": "EAf2Qn1kNix6gdiaEviWqzKwKtJUJTXTRT3nZLYcV9QY",
"baseToken": {
"address": "FT6ZnLbmaQbUmxbpe69qwRgPi9tU8QGY8S7gqt4Wbonk",
"name": "BIG",
"symbol": "BIG",
"decimals": 6
},
"quoteToken": {
"address": "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB",
"name": "World Liberty Financial USD",
"symbol": "USD1",
"decimals": 6
},
"quoteTokenSymbol": "USD1",
"price": "0.001820",
"priceUsd": "0.001820",
"priceUSD": "0.001820",
"txns": {
"m5": { "buys": 68, "sells": 28 },
"h1": { "buys": 1333, "sells": 1064 },
"h6": { "buys": 32404, "sells": 29022 },
"h24": { "buys": 35242, "sells": 32222 }
},
"buyers": { "m5": 51, "h1": 622, "h6": 7600, "h24": 8290 },
"sellers": { "m5": 27, "h1": 565, "h6": 6549, "h24": 6859 },
"makers": { "m5": 76, "h1": 996, "h6": 8906, "h24": 9249 },
"volume": { "m5": 9933.02, "h1": 296433.65, "h6": 9458621.98, "h24": 9981693.42 },
"volumeBuy": { "m5": 4850, "h1": 144929.23, "h6": 4722534.19, "h24": 4996787.53 },
"volumeSell": { "m5": 5083.01, "h1": 151504.42, "h6": 4736087.78, "h24": 4984905.89 },
"priceChange": { "m5": -0.65, "h1": -8.89, "h6": 239, "h24": 2582 },
"liquidity": { "usd": 151707.33, "base": 41663892, "quote": 75853 },
"marketCap": 1820609,
"fdv": 1820609,
"pairCreatedAt": 1765041438000,
"pairCreatedAtRaw": { "seconds": 1765041438, "nanos": 0 },
"profile": {
"eti": true,
"header": true,
"website": true,
"twitter": true,
"linkCount": 2,
"imgKey": "2da648"
},
"cmsProfile": {
"headerId": "4a2b4e6e0640ededdd9b3dab20d9f6900bddcc6878fe732d38c9f9cc80d98efc",
"iconId": "82678f55db554c1d61b069daf596aeef6f1d665ee3a12f261b144c0326c9c17f",
"description": "This is going to be $BIG",
"links": [
{ "label": "Website", "url": "https://truthsocial.com/@unknown/posts/115673738803217830" },
{ "type": "twitter", "url": "https://x.com/i/communities/1997366922938126733" }
],
"nsfw": false
},
"isBoostable": true,
"c": "a",
"a": "solamm"
},
{
"chainId": "solana",
"dexId": "pumpswap",
"pairAddress": "8wXzwpLjk6QJMYYC1VHueNnxRVW2nFGvQjgEnV4Mv8sY",
"baseToken": {
"address": "CSrwNk6B1DwWCHRMsaoDVUfD5bBMQCJPY72ZG3Nnpump",
"name": "Franklin The Turtle",
"symbol": "Franklin",
"decimals": 6
},
"quoteToken": {
"address": "So11111111111111111111111111111111111111112",
"name": "Wrapped SOL",
"symbol": "SOL",
"decimals": 9
},
"quoteTokenSymbol": "SOL",
"price": "0.00007098",
"priceUsd": "0.009336",
"priceUSD": "0.009336",
"txns": {
"m5": { "buys": 57, "sells": 54 },
"h1": { "buys": 7924, "sells": 799 },
"h6": { "buys": 13090, "sells": 5776 },
"h24": { "buys": 37491, "sells": 28148 }
},
"buyers": { "m5": 49, "h1": 7196, "h6": 8713, "h24": 13450 },
"sellers": { "m5": 46, "h1": 465, "h6": 2077, "h24": 6558 },
"makers": { "m5": 94, "h1": 7543, "h6": 9818, "h24": 15675 },
"volume": { "m5": 17675.25, "h1": 334508.48, "h6": 2505119.75, "h24": 12825807.97 },
"volumeBuy": { "m5": 8714.96, "h1": 156717.8, "h6": 1242301.69, "h24": 6470078.63 },
"volumeSell": { "m5": 8960.28, "h1": 177790.68, "h6": 1262818.06, "h24": 6355729.34 },
"priceChange": { "m5": -0.34, "h1": -19.48, "h6": -22.12, "h24": 223 },
"liquidity": { "usd": 403605.74, "base": 21566984, "quote": 1537.8354 },
"marketCap": 9335780,
"fdv": 9335780,
"pairCreatedAt": 1764588851000,
"pairCreatedAtRaw": { "seconds": 1764588851, "nanos": 0 },
"profile": {
"eti": true,
"header": true,
"website": true,
"twitter": true,
"linkCount": 2,
"imgKey": "731b40"
},
"isBoostable": true,
"c": "a",
"a": "pumpfundex",
"launchpad": {
"progress": 100,
"creator": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
"migrationDex": "pumpswap",
"meta": { "id": "pumpfun" }
}
}
],
"event_type": "pairs",
"timestamp": "2026-01-21T08:28:36.113Z"
}type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';The CLI supports configuration files for easier management of settings across environments.
Generate a default configuration file:
node dist/cli.cjs --initThis creates .dexrtrc.json or .dexrtrc.yaml in your current directory.
JSON Format (.dexrtrc.json):
{
"baseUrl": "https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor",
"apiToken": "apify_api_xxxxxxxxxxxxx",
"pageUrls": [
"https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc"
],
"mode": "jsonl",
"output": {
"jsonlPath": "./events.jsonl",
"compression": {
"enabled": true,
"level": 6
},
"rotation": {
"maxSizeMB": 100,
"interval": "daily"
}
}
}YAML Format (.dexrtrc.yaml):
# DexScreener Client Configuration
baseUrl: https://muhammetakkurtt--dexscreener-realtime-monitor.apify.actor
apiToken: apify_api_xxxxxxxxxxxxx
pageUrls:
- https://dexscreener.com/solana?rankBy=trendingScoreH6&order=desc
mode: jsonl
output:
jsonlPath: ./events.jsonl
compression:
enabled: true
level: 6
rotation:
maxSizeMB: 100
interval: dailyUse profiles for different environments:
{
"profiles": {
"dev": {
"baseUrl": "https://dev-actor.apify.actor",
"pageUrls": ["https://dexscreener.com/solana"],
"mode": "stdout"
},
"prod": {
"baseUrl": "https://prod-actor.apify.actor",
"pageUrls": [
"https://dexscreener.com/solana",
"https://dexscreener.com/ethereum"
],
"mode": "webhook",
"output": {
"webhookUrl": "https://api.example.com/events"
}
}
},
"default": "dev"
}Use a specific profile:
node dist/cli.cjs --profile prodCheck your configuration without starting streams:
node dist/cli.cjs --validate- π Real-time Streaming - WebSocket connection with automatic reconnection
- π Flexible Authentication - Multiple auth modes (header, query, both, auto-fallback)
- π§ Protocol Normalization - Automatic HTTP/HTTPS to WS/WSS conversion
- π Multi-Stream Support - Monitor multiple pages/chains simultaneously
- π Advanced Filtering - Filter pairs by chain, liquidity, volume, price change
- π§ Data Transformation - Select and reshape fields with aliases
- π Monitoring - Prometheus metrics, health checks, structured logging
- π¦ Output Management - Compression, rotation, batching, throttling, sampling
- β‘ Production Ready - Error handling, graceful shutdown, keep-alive management
For detailed guides and examples, see the User Guide.
import { FilterBuilder } from './dist/index.js';
const filter = FilterBuilder.combineFilters([
FilterBuilder.chainFilter(['solana', 'ethereum']),
FilterBuilder.liquidityFilter(50000),
FilterBuilder.volumeFilter('h24', 100000)
], 'AND');import { Transformer } from './dist/index.js';
const transformer = new Transformer({
fields: ['baseToken.symbol', 'priceUsd', 'volume.h24'],
aliases: { 'baseToken.symbol': 'symbol', 'priceUsd': 'price' }
});See User Guide for complete filtering and transformation examples.
The SDK provides production-ready monitoring capabilities:
- Health Checks - HTTP endpoint for health status
- Prometheus Metrics - Export metrics for monitoring
- Structured Logging - JSON/text logging with levels
- Performance Monitoring - Track processing duration and memory
- Alerts - Threshold-based alerting
Quick Example:
import { HealthChecker, MetricsCollector, StructuredLogger } from './dist/index.js';
const health = new HealthChecker(3000);
const metrics = new MetricsCollector();
const logger = new StructuredLogger('info', 'json');
health.start(); // http://localhost:3000/healthCLI Usage:
node dist/cli.cjs \
--page-url "https://dexscreener.com/solana" \
--health-port 3000 \
--metrics-port 9090 \
--log-level info \
--log-format jsonSee User Guide for detailed monitoring setup and API Reference for complete API documentation.
| Flag | Description | Default |
|---|---|---|
--base-url |
Apify Actor base URL | Required |
--api-token |
Apify API token (or use APIFY_TOKEN env) |
Required |
--page-url |
DexScreener page URL(s) to monitor | Required |
--mode |
Output mode: stdout, jsonl, webhook |
stdout |
--retry-ms |
Reconnection delay in milliseconds | 3000 |
--keep-alive-ms |
Health check interval in milliseconds | 120000 |
| Flag | Description |
|---|---|
--init |
Generate default configuration file |
--profile <name> |
Use specific configuration profile |
--validate |
Validate configuration and exit |
--interactive |
Launch interactive configuration wizard |
| Flag | Description |
|---|---|
--jsonl-path <path> |
File path for JSONL output |
--webhook-url <url> |
Webhook URL for HTTP POST |
--compress |
Enable gzip compression |
--rotate-size <mb> |
Rotate file when size exceeds MB |
--rotate-interval <interval> |
Rotate file by time: hourly, daily |
--batch-size <n> |
Batch size for webhook mode |
--batch-interval <ms> |
Batch interval in milliseconds |
--sample-rate <percent> |
Sample rate percentage (0-100) |
| Flag | Description |
|---|---|
--health-port <port> |
HTTP health check endpoint port |
--metrics-port <port> |
Prometheus metrics endpoint port |
--log-level <level> |
Log level: error, warn, info, debug |
--log-format <format> |
Log format: text, json |
--perf |
Enable performance monitoring |
--verbose |
Verbose output mode |
--quiet |
Quiet mode (errors only) |
--debug |
Debug mode with detailed diagnostics |
- User Guide - Complete tutorials, examples, and best practices
- API Reference - Technical reference for all classes and methods
- Examples - Working code examples
# Run tests
npm test
# Type check
npm run typecheck
# Watch mode for development
npm run dev