Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mobula.io/llms.txt

Use this file to discover all available pages before exploring further.

This endpoint is only available to Growth and Enterprise plans.
Not suitable for real-time chart animation.token-details is an enriched but slow stream — it delivers aggregated token data (volumes, holder stats, price changes, etc.) recomputed on each trade, which introduces significant processing latency. Updates may arrive up to 1 second after a trade.If you want to animate a live price chart, use fast-trade instead:
  • Trades arrive in near real-time (sub-second latency)
  • Minimal payload — price, amount, type, hash
  • Designed for high-frequency rendering
Use token-details for dashboards, token screeners, or any UI that needs enriched stats (volume, buys/sells counts, holder percentages) but does not require tick-level speed.

Endpoint Details

  • URL: wss://api.mobula.io
  • Event Type: token-details

Subscription Formats

The token-details endpoint supports subscribing to multiple token addresses for real-time token data. Subscribe to multiple tokens using the tokens array format:
{
  "type": "token-details",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "tokens": [
      {
        "blockchain": "solana",
        "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
      },
      {
        "blockchain": "solana",
        "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      },
      {
        "blockchain": "evm:1",
        "address": "0x1234567890abcdef1234567890abcdef12345678"
      }
    ],
    "subscriptionTracking": true
  }
}

Parameters

  • tokens (required): Array of token subscription items, each containing:
    • blockchain (required): Blockchain identifier (e.g., "evm:1", "solana")
    • address (required): Token address
  • subscriptionId (optional): Unique identifier for your WebSocket connection. Auto-generated if not provided
  • subscriptionTracking (optional, default: false): Include subscription details in response logs for debugging
  • maxUpdatesPerMinute (optional): Limit how often the server sends updates for each token. See Update Rate Throttling below
  • filterOutliers (optional, default: false): When true, drops dust trades and price outliers that would otherwise overwrite tokenData.priceUSD in the broadcast payload. See Outlier Filtering below
Token Addresses Only: This endpoint is for subscribing to token addresses. For pool-specific subscriptions, use the market-details endpoint instead.

Real-Time Updates

After subscribing, you’ll receive real-time updates whenever new trades occur involving the subscribed tokens.

Real-Time Trade Updates for Token Subscriptions

When trades happen involving subscribed tokens, you’ll receive updates with trade information:
{
  "pair": "0x1234567890abcdef1234567890abcdef12345678",
  "date": 1704067800000,
  "token_price": 0.0234,
  "token_price_vs": 2500.75,
  "token_amount": 1000.0,
  "token_amount_vs": 0.4,
  "token_amount_usd": 23.40,
  "type": "sell",
  "operation": "regular",
  "blockchain": "Ethereum",
  "hash": "0xdef789abc012...",
  "sender": "0x1234567890abcdef...",
  "token_amount_raw": "1000000000000000000000",
  "token_amount_raw_vs": "400000000000000000",
  "labels": ["sniper", "proTrader"],
  "walletMetadata": {
    "entityName": "Wintermute",
    "entityLogo": "https://example.com/wintermute.png",
    "entityLabels": ["market-maker"],
    "entityType": "market_maker",
    "entityDescription": "Wintermute is a leading global algorithmic trading firm",
    "entityTwitter": "https://twitter.com/wintermute_t",
    "entityWebsite": "https://wintermute.com",
    "entityGithub": null,
    "entityDiscord": null,
    "entityTelegram": null
  },
  "preBalanceBaseToken": "5000000000000000000000",
  "preBalanceQuoteToken": "2000000000000000000",
  "postBalanceBaseToken": "4000000000000000000000",
  "postBalanceQuoteToken": "2400000000000000000",
  "swapRecipient": null,
  "platform": "axiom",
  "platformMetadata": {
    "id": "axiom",
    "name": "Axiom",
    "logo": "https://example.com/axiom.png"
  },
      "tokenData": {
    // Complete EnrichedTokenData structure with all fields from TokenDetailsOutput.ts
    "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump",
    "chainId": "solana",
    "symbol": "EXAMPLE",
    "name": "Example Token",
    "decimals": 9,
    "id": 12345,
    "priceUSD": 0.0234,
    "priceToken": 43.21,
    "priceTokenString": "43.210000000000000000000000000000",
    "approximateReserveUSD": 125000.50,
    "approximateReserveTokenRaw": "5000000000000000000",
    "approximateReserveToken": 5000.0,
    "totalSupply": 1000000000,
    "circulatingSupply": 800000000,
    "marketCapUSD": 18720000,
    "marketCapDilutedUSD": 23400000,
    "logo": "https://example.com/token-logo.png",
    "exchange": {
      "name": "Uniswap V3",
      "logo": "https://example.com/uniswap-logo.png"
    },
    "factory": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
    "source": "uniswap",
    "sourceFactory": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
    "liquidityUSD": 250000.75,
    "liquidityMaxUSD": 300000.0,
    "bonded": false,
    "bondingPercentage": 0,
    "bondingCurveAddress": null,
    "preBondingFactory": null,
    "poolAddress": null,
    "top10HoldingsPercentage": 45.6,
    "top50HoldingsPercentage": 68.9,
    "top100HoldingsPercentage": 78.5,
    "top200HoldingsPercentage": 85.2,
    "devHoldingsPercentage": 15.5,
    "insidersHoldingsPercentage": 8.2,
    "bundlersHoldingsPercentage": 2.8,
    "snipersHoldingsPercentage": 3.1,
    "proTradersHoldingsPercentage": 5.0,
    "blockchain": "solana",
    "type": "token",
    "deployer": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump",
    "bondedAt": null,
    "athUSD": 0.0456,
    "atlUSD": 0.0012,
    "athDate": "2024-12-15T10:30:00.000Z",
    "atlDate": "2024-12-01T00:00:00.000Z",
    "priceChange1minPercentage": 0.5,
    "priceChange5minPercentage": -1.2,
    "priceChange1hPercentage": 3.4,
    "priceChange4hPercentage": 2.1,
    "priceChange6hPercentage": 1.8,
    "priceChange12hPercentage": 0.9,
    "priceChange24hPercentage": -5.8,
    "liquidityBurnPercentage": 100,
    "volume1minUSD": 1250.75,
    "volume5minUSD": 8500.25,
    "volume15minUSD": 42500.80,
    "volume1hUSD": 45000.80,
    "volume4hUSD": 180000.25,
    "volume6hUSD": 270000.40,
    "volume12hUSD": 540000.50,
    "volume24hUSD": 890000.50,
    "volumeBuy1minUSD": 675.50,
    "volumeBuy5minUSD": 4500.25,
    "volumeBuy15minUSD": 22500.40,
    "volumeBuy1hUSD": 24000.40,
    "volumeBuy4hUSD": 96000.15,
    "volumeBuy6hUSD": 144000.25,
    "volumeBuy12hUSD": 288000.30,
    "volumeBuy24hUSD": 480000.25,
    "volumeSell1minUSD": 575.25,
    "volumeSell5minUSD": 4000.00,
    "volumeSell15minUSD": 20000.40,
    "volumeSell1hUSD": 21000.40,
    "volumeSell4hUSD": 84000.10,
    "volumeSell6hUSD": 126000.15,
    "volumeSell12hUSD": 252000.20,
    "volumeSell24hUSD": 410000.25,
    "trades1min": 15,
    "trades5min": 85,
    "trades15min": 425,
    "trades1h": 450,
    "trades4h": 1800,
    "trades6h": 2700,
    "trades12h": 5400,
    "trades24h": 8900,
    "buys1min": 8,
    "buys5min": 45,
    "buys15min": 225,
    "buys1h": 240,
    "buys4h": 960,
    "buys6h": 1440,
    "buys12h": 2880,
    "buys24h": 4800,
    "sells1min": 7,
    "sells5min": 40,
    "sells15min": 200,
    "sells1h": 210,
    "sells4h": 840,
    "sells6h": 1260,
    "sells12h": 2520,
    "sells24h": 4100,
    "buyers1min": 8,
    "buyers5min": 42,
    "buyers15min": 210,
    "buyers1h": 235,
    "buyers4h": 940,
    "buyers6h": 1410,
    "buyers12h": 2820,
    "buyers24h": 4650,
    "sellers1min": 7,
    "sellers5min": 38,
    "sellers15min": 190,
    "sellers1h": 205,
    "sellers4h": 820,
    "sellers6h": 1230,
    "sellers12h": 2460,
    "sellers24h": 3950,
    "traders1min": 14,
    "traders5min": 78,
    "traders15min": 390,
    "traders1h": 420,
    "traders4h": 1680,
    "traders6h": 2520,
    "traders12h": 5040,
    "traders24h": 8200,
    "feesPaid1minUSD": 6.25,
    "feesPaid5minUSD": 42.5,
    "feesPaid15minUSD": 212.5,
    "feesPaid1hUSD": 225.0,
    "feesPaid4hUSD": 900.0,
    "feesPaid6hUSD": 1350.0,
    "feesPaid12hUSD": 2700.0,
    "feesPaid24hUSD": 4450.0,
    "totalFeesPaidUSD": 8900.0,
    "organicTrades1min": 12,
    "organicTrades5min": 68,
    "organicTrades15min": 340,
    "organicTrades1h": 360,
    "organicTrades4h": 1440,
    "organicTrades6h": 2160,
    "organicTrades12h": 4320,
    "organicTrades24h": 7120,
    "organicTraders1min": 11,
    "organicTraders5min": 62,
    "organicTraders15min": 310,
    "organicTraders1h": 335,
    "organicTraders4h": 1340,
    "organicTraders6h": 2010,
    "organicTraders12h": 4020,
    "organicTraders24h": 6560,
    "organicVolume1minUSD": 1000.6,
    "organicVolume5minUSD": 6800.2,
    "organicVolume15minUSD": 34000.64,
    "organicVolume1hUSD": 36000.64,
    "organicVolume4hUSD": 144000.2,
    "organicVolume6hUSD": 216000.32,
    "organicVolume12hUSD": 432000.4,
    "organicVolume24hUSD": 712000.4,
    "organicVolumeBuy1minUSD": 540.32,
    "organicVolumeBuy5minUSD": 3600.16,
    "organicVolumeBuy15minUSD": 18000.32,
    "organicVolumeBuy1hUSD": 19200.32,
    "organicVolumeBuy4hUSD": 76800.12,
    "organicVolumeBuy6hUSD": 115200.2,
    "organicVolumeBuy12hUSD": 230400.24,
    "organicVolumeBuy24hUSD": 384000.2,
    "organicVolumeSell1minUSD": 460.28,
    "organicVolumeSell5minUSD": 3200.04,
    "organicVolumeSell15minUSD": 16000.32,
    "organicVolumeSell1hUSD": 16800.32,
    "organicVolumeSell4hUSD": 67200.08,
    "organicVolumeSell6hUSD": 100800.12,
    "organicVolumeSell12hUSD": 201600.16,
    "organicVolumeSell24hUSD": 328000.2,
    "organicBuys1min": 6,
    "organicBuys5min": 36,
    "organicBuys15min": 180,
    "organicBuys1h": 192,
    "organicBuys4h": 768,
    "organicBuys6h": 1152,
    "organicBuys12h": 2304,
    "organicBuys24h": 3840,
    "organicSells1min": 6,
    "organicSells5min": 32,
    "organicSells15min": 160,
    "organicSells1h": 168,
    "organicSells4h": 672,
    "organicSells6h": 1008,
    "organicSells12h": 2016,
    "organicSells24h": 3280,
    "organicBuyers1min": 6,
    "organicBuyers5min": 33,
    "organicBuyers15min": 165,
    "organicBuyers1h": 188,
    "organicBuyers4h": 752,
    "organicBuyers6h": 1128,
    "organicBuyers12h": 2256,
    "organicBuyers24h": 3720,
    "organicSellers1min": 6,
    "organicSellers5min": 30,
    "organicSellers15min": 152,
    "organicSellers1h": 164,
    "organicSellers4h": 656,
    "organicSellers6h": 984,
    "organicSellers12h": 1968,
    "organicSellers24h": 3168,
    "createdAt": "2024-12-01T00:00:00.000Z",
    "latestPriceUSD": 0.0234,
    "holdersCount": 1250,
    "description": "Example token description",
    "socials": {
      "twitter": "https://twitter.com/example",
      "website": "https://example.com",
      "telegram": "https://t.me/example",
      "others": {},
      "uri": "https://example.com/token"
    },
    "security": {
      "buyTax": "5.00",
      "sellTax": "10.00",
      "transferPausable": false,
      "top10Holders": "42.50",
      "isBlacklisted": false,
      "isHoneypot": false,
      "isNotOpenSource": false,
      "renounced": true,
      "locked": "80.00",
      "isWhitelisted": false,
      "balanceMutable": false,
      "lowLiquidity": "false",
      "burnRate": "2.50",
      "isMintable": false,
      "modifyableTax": false,
      "selfDestruct": false,
      "liquidityBurnPercentage": 75.5,
      "noMintAuthority": true
    },
    "twitterReusesCount": 0,
    "twitterRenameCount": 0,
    "twitterRenameHistory": [],
    "deployerMigrationsCount": 0,
    "dexscreenerListed": true,
    "dexscreenerHeader": null,
    "dexscreenerAdPaid": false
  },
  "subscriptionId": "sub_abc123",
  "updated": true,
  "timestamp": 1704067800000
}

Optional Fields Behavior

Fields may be entirely absent, not null: Several fields in trade update messages are conditionally included — when there is no data, the field is completely omitted from the JSON payload rather than being set to null or an empty value.This applies to:
  • labels: Only present when the sender wallet has known labels for this token. If absent, the sender has no labels.
  • walletMetadata: Only present when the sender wallet has entity metadata (e.g., known entity like Wintermute, Binance). If absent, no entity info is available.
Always use optional chaining or check for field existence:
// Correct
const labels = data.labels ?? [];
const entityName = data.walletMetadata?.entityName ?? null;

// Incorrect — will throw if field is missing
const labels = data.labels; // undefined, not []

Security Object Fields

All fields in tokenData.security are optional — only present when data is available for the token. A missing field means the check was not performed, not that the token is safe.
EVM vs Solana availability: Most fields apply to EVM chains. noMintAuthority is Solana-specific. isHoneypot, isNotOpenSource, renounced, locked, isWhitelisted, isMintable, modifyableTax, and selfDestruct are EVM-only.
FieldTypeDescription
buyTaxstringBuy fee as a percentage string (e.g. "5.00" = 5%). Present when a transfer tax is charged on buys.
sellTaxstringSell fee as a percentage string (e.g. "10.00" = 10%). Present when a transfer tax is charged on sells.
transferPausablebooleanWhether the contract owner can pause all token transfers.
top10HoldersstringPercentage of total supply held by the top 10 wallets (e.g. "42.50").
isBlacklistedbooleanWhether the contract contains a blacklist mechanism that can block specific addresses from trading.
isHoneypotbooleanWhether the token is a honeypot — users can buy but cannot sell. (EVM only)
isNotOpenSourcebooleanWhether the contract source code is not verified/open source on-chain. (EVM only)
renouncedbooleanWhether contract ownership has been renounced (owner is the zero address). (EVM only)
lockedstringPercentage of liquidity that is locked (e.g. "80.00"). (EVM only)
isWhitelistedbooleanWhether the contract has a whitelist mechanism restricting who can trade. (EVM only)
balanceMutablebooleanWhether the contract owner can arbitrarily modify token balances.
lowLiquiditystringIndicates low liquidity status (e.g. "true" or "false").
burnRatestringPercentage of each transaction automatically burned (e.g. "2.50").
isMintablebooleanWhether the contract allows minting new tokens after deployment. (EVM only)
modifyableTaxbooleanWhether the contract owner can change the buy/sell tax rates. (EVM only)
selfDestructbooleanWhether the contract contains a self-destruct function. (EVM only)
liquidityBurnPercentagenumberPercentage of liquidity pool tokens that have been burned (legacy — prefer the top-level liquidityBurnPercentage described below).
noMintAuthoritybooleanWhether the mint authority has been disabled (safe = true). (Solana only)

Liquidity Burn

FieldTypeDescription
liquidityBurnPercentagenumber | nullShare of LP tokens (V2) or position liquidity (V3/V4) held by dead/locker addresses for the largest pool. Computed from on-chain LP holder balances — no longer sourced from GoPlus. null until the LP-burn cache is warmed for this token. Range 0100.

Unsubscribing from the Stream

Unsubscribe from All Token-Details Streams

{
  "type": "unsubscribe",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "type": "token-details"
  }
}

Unsubscribe from Specific Subscription

{
  "type": "unsubscribe",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "subscriptionId": "sub_abc123"
  }
}

Unsubscribe from Specific Subscription with Type

{
  "type": "unsubscribe",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "type": "token-details",
    "subscriptionId": "sub_abc123"
  }
}

Unsubscribe from Specific Tokens (Partial Unsubscription)

You can unsubscribe from specific tokens while keeping others active in the same subscription:
{
  "type": "unsubscribe",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "type": "token-details",
    "tokens": [
      {
        "blockchain": "solana",
        "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
      }
    ]
  }
}

Unsubscribe from Multiple Specific Tokens

{
  "type": "unsubscribe",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "type": "token-details",
    "tokens": [
      {
        "blockchain": "solana",
        "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
      },
      {
        "blockchain": "evm:1",
        "address": "0x1234567890abcdef1234567890abcdef12345678"
      }
    ]
  }
}

Unsubscribe from Specific Tokens in a Specific Subscription

{
  "type": "unsubscribe",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "type": "token-details",
    "subscriptionId": "sub_abc123",
    "tokens": [
      {
        "blockchain": "solana",
        "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
      }
    ]
  }
}
If you didn’t provide a subscriptionId when subscribing, one is auto-generated. To retrieve it, set "subscriptionTracking": true in the subscription payload.For partial unsubscriptions without subscriptionId, the system will automatically find and modify subscriptions that contain the specified tokens, leaving other tokens in those subscriptions active.

Update Rate Throttling

You can control how frequently the server sends update messages per token using the maxUpdatesPerMinute field. This is useful for reducing bandwidth on dashboards that don’t need every single trade update.
{
  "type": "token-details",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "tokens": [
      {
        "blockchain": "solana",
        "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
      }
    ],
    "maxUpdatesPerMinute": 60
  }
}

How It Works

Despite the name, maxUpdatesPerMinute does not set a budget that gets consumed over a minute. Instead, it sets a minimum interval between delivered updates per token:
interval = 60 000ms / maxUpdatesPerMinute
For example, maxUpdatesPerMinute: 100 → minimum interval of 600ms between two delivered updates for the same token.
  • Interval-based, not budget-based: The server enforces a cooldown between updates. Once an update is delivered, all subsequent updates for that token are dropped until the interval elapses. There is no counter that depletes — after each delivered update, the cooldown simply restarts.
  • Per-token throttle: Each token is throttled independently. Subscribing to 5 tokens with maxUpdatesPerMinute: 60 means each token sends at most 1 update/second.
  • Dropped, not buffered: When an update arrives during the cooldown, it is silently dropped — not queued or aggregated. The next update arriving after the interval has elapsed will be delivered normally.

Burst Behavior Example

With maxUpdatesPerMinute: 100 (interval = 600ms), if a token receives a burst of 50 updates in 1 second:
TimeEventResult
t=0msUpdate 1 arrivesDelivered (cooldown starts)
t=50msUpdates 2–10 arriveDropped (still within 600ms cooldown)
t=600msUpdate ~20 arrivesDelivered (cooldown elapsed, restarts)
t=800msUpdates ~25–50 arriveDropped (still within new 600ms cooldown)
t=2sNext update arrivesDelivered normally
You receive ~2 updates from the burst, and updates resume normally afterwards. There is no debt or penalty — the throttle does not “remember” dropped updates or block future delivery.

Reference Values

maxUpdatesPerMinuteEffective intervalDescription
600100msMaximum rate (~10 updates/sec per token)
601sOne update per second per token
610sOne update every 10 seconds
160sOne update per minute (minimum rate)
When maxUpdatesPerMinute is not provided, no throttle is applied and updates are delivered as fast as they arrive.

Outlier Filtering

When filterOutliers is set to true, the server applies the same outlier detection used by the fast-trade and OHLCV streams. This is important for token-details specifically: each broadcast trade overrides tokenData.priceUSD in the payload with that trade’s computed price — so a single dust or low-liquidity swap can otherwise pollute your UI price.
{
  "type": "token-details",
  "authorization": "YOUR-API-KEY",
  "payload": {
    "filterOutliers": true,
    "tokens": [
      {
        "blockchain": "solana",
        "address": "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
      }
    ]
  }
}

What Gets Filtered

FilterConditionPurpose
Dust tradesamountUSD <= $0.10Removes near-zero trades that can print false prices
Non-regular swapsswapType != REGULARRemoves LP deposits/withdrawals
Extreme pricesPrice > 3.4e30 or < 1e-16Removes UniV3/CLMM edge-case prices
Low-volume multi-poolamountUSD <= $100 on tokens with multiple poolsRemoves small trades on low-liquidity pools that deviate from the market price
When filterOutliers is false (default), every trade is delivered — including dust and edge-case prints. Existing clients keep the same behavior as before this flag was introduced.
If every trade in a batch gets filtered out, the stream falls back to a compact snapshot message { token, subscriptionId, updated: true, timestamp } (no hash/token_price fields). Your UI still gets a refreshed server-side aggregate price instead of a polluted per-trade print.

Implementation Example

const socket = new WebSocket("wss://api.mobula.io");

socket.addEventListener("open", () => {
  socket.send(JSON.stringify({
    type: "token-details",
    authorization: "YOUR_API_KEY",
    payload: {
      tokens: [
        {
          blockchain: "solana",
          address: "3vz82EWYv8xnc7Cm7qSgERcpMeqw92PcX8PBz88npump"
        }
      ],
      subscriptionTracking: true
    }
  }));
});

socket.addEventListener("message", (event) => {
  const data = JSON.parse(event.data);
  
  // Process token update
  if (data.tokenData) {
    console.log("Token update:", data.tokenData);
    console.log(`Price: $${data.tokenData.priceUSD}`);
    console.log(`24h Volume: $${data.tokenData.volume24hUSD}`);
  }
});

socket.addEventListener("error", (error) => {
  console.error("WebSocket error:", error);
});

socket.addEventListener("close", () => {
  console.log("WebSocket connection closed");
});
You can use the Network tab in your browser to see the WebSocket requests and responses in real-time.

Connection Keepalive (Ping/Pong)

To maintain active WebSocket connections and prevent timeouts, you can use the ping/pong mechanism: Send ping:
{"event":"ping"}
Receive pong: The server will respond with a pong message to confirm the connection is active.
Use ping messages periodically (every 30-60 seconds) to keep long-lived connections alive.

Support

Can’t find what you’re looking for? Reach out to us, response times < 1h.

Support

Telegram

Support

Slack

Need help?

Email