When working with blockchain data, you’ll encounter numerous tokens with varying levels of liquidity and utility. Sim APIs provide comprehensive token metadata and liquidity data to help you implement custom filtering logic that fits your specific requirements.

Our Approach to Token Data

We don’t offer direct spam filtering because definitions of spam are varying and subjective. Instead, we provide the best objective measure that we’re aware of—liquidity data—to allow developers to filter downstream based on their specific requirements.

By grounding filtering decisions in measurable, onchain liquidity, rather than subjective labels, our method offers several advantages:

  • Objective: We provide liquidity metrics rather than subjective spam classifications
  • Realtime: Liquidity is checked at query time, not based on outdated lists
  • Flexible: All filtering data is provided, allowing you to implement custom logic that fits your use case
  • Transparent: You have full visibility into the data used for filtering decisions
  • Adaptable: Your filtering criteria can evolve with your application’s needs

Supported APIs

For each token in our responses, we include comprehensive metadata that gives you the information needed to make informed filtering decisions:

  1. Token basics: symbol, name, and decimals properties
  2. Price data: Current USD pricing information using price_usd
  3. Liquidity information: Real-time liquidity pool data with pool_size
  4. Pool size: The total value locked in the token’s highest liquidity pool using low_liquidity

How Sim Calculates Liquidity Data

Sim’s approach to assessing liquidity is sophisticated and real-time:

  • For each token, we dynamically track the highest liquidity route to USDC
  • We calculate the USD value of the liquidity along that route for each token upon each query
  • This provides you with current, accurate liquidity information rather than static or outdated data

When pool_size is very small and low_liquidity is true, you should disregard price_usd and value_usd fields. While the price is technically correct based on the best liquidity pool we can find, it becomes effectively meaningless when there’s insufficient liquidity. You can’t actually exchange the token for anything else of value at that price.

Using Token Data for Custom Filtering

Let’s explore practical implementations for different filtering scenarios. The following examples demonstrate how to use this data to create robust filtering logic that meets your app’s needs.

Liquidity Threshold Filtering

Filter tokens based on minimum liquidity requirements using the pool_size field. This is one of the most effective ways to filter out low-quality tokens.

// Filter tokens with at least $10,000 in liquidity
const filterByLiquidity = (tokens, minLiquidity = 10000) => {
  return tokens.filter(token => {
    return token.pool_size && token.pool_size >= minLiquidity;
  });
};

// Usage
const filteredTokens = filterByLiquidity(tokenData, 10000);

This approach ensures you only show tokens that have sufficient trading volume and market depth, reducing the likelihood of displaying illiquid or potentially problematic tokens.

Allowlisting Specific Tokens

Include certain tokens regardless of their liquidity metrics by maintaining a list of approved token addresses and chain IDs.

A secure method for allowlisting is to use a combination of the token’s unique contract address and its chain_id. This guarantees you are identifying the exact token on the correct network.

// Allowlist of trusted tokens. Each entry is an object containing
// the specific chainId and the token's contract address.
const ALLOWLIST = [
  // USDC on Ethereum
  { chainId: 1, address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' },
  // Wrapped BTC on Ethereum
  { chainId: 1, address: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599' },
  // DEGEN on Base
  { chainId: 8453, address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed' },
  // Native ETH on Ethereum Mainnet
  { chainId: 1, address: 'native' },
   // Native ETH on Base
  { chainId: 8453, address: 'native' },
];

const filterWithAllowlist = (tokens, minLiquidity = 10000) => {
  return tokens.filter(token => {
    // Check if the current token matches any in our allowlist.
    const isAllowlisted = ALLOWLIST.some(allowlistItem => {
      // Note: The Balances API uses `address`, while the Activity API uses `token_address`.
      // We handle both possibilities here. We also convert to lowercase for a reliable match.
      const tokenAddress = (token.address || token.token_address || '').toLowerCase();
      
      return token.chain_id === allowlistItem.chainId && tokenAddress === allowlistItem.address;
    });

    // 1. If the token is on the allowlist, always include it.
    if (isAllowlisted) {
      return true;
    }
    
    // 2. For all other tokens, apply a standard liquidity filter.
    return token.pool_size && token.pool_size >= minLiquidity;
  });
};

Denylisting Specific Tokens

Exclude certain tokens even if they meet your liquidity criteria by maintaining a blocklist of problematic symbols, or any other criteria you choose.

// Denylist of problematic token symbols
const DENYLISTED_SYMBOLS = [
  'SCAM',
  'RUG',
];

const filterWithDenylist = (tokens) => {
  return tokens.filter(token => {
    // Exclude denylisted tokens
    if (token.symbol && DENYLISTED_SYMBOLS.includes(token.symbol.toUpperCase())) {
      return false;
    }
    // Apply other filtering criteria
    return token.pool_size && token.pool_size >= 1000;
  });
};

Denylisting helps you maintain control over which tokens appear in your application, even if they have sufficient liquidity.

Custom Criteria Combinations

Create sophisticated filtering logic by combining multiple criteria such as liquidity, completeness, and custom business rules.

const advancedTokenFilter = (tokens, options = {}) => {
  const {
    minLiquidity = 1000,
    requireCompleteName = true,
    minPriceUsd = 0.000001,
    allowLowLiquidity = false
  } = options;

  return tokens.filter(token => {
    // Check if token has complete metadata
    if (requireCompleteName && (!token.name || !token.symbol)) {
      return false;
    }

    // Check minimum price threshold
    if (token.price_usd && token.price_usd < minPriceUsd) {
      return false;
    }

    // Check liquidity requirements
    if (!allowLowLiquidity && token.low_liquidity) {
      return false;
    }

    if (token.pool_size && token.pool_size < minLiquidity) {
      return false;
    }

    return true;
  });
};

This approach allows you to create nuanced filtering that considers multiple factors simultaneously.

Token Completeness Filtering

Filter based on whether tokens have complete metadata, ensuring users only see tokens with proper names and symbols.

const filterCompleteTokens = (tokens) => {
  return tokens.filter(token => {
    // Require all basic metadata to be present
    const hasBasicInfo = token.name && 
                        token.symbol && 
                        token.decimals !== undefined;
    
    // Optionally require price data
    const hasPriceData = token.price_usd !== undefined;
    
    return hasBasicInfo && hasPriceData;
  });
};

// Or create a more flexible version
const filterTokensByCompleteness = (tokens, strict = false) => {
  return tokens.filter(token => {
    if (strict) {
      // Strict mode: require all fields
      return token.name && token.symbol && token.decimals && 
             token.price_usd && token.pool_size;
    } else {
      // Lenient mode: require only basic fields
      return token.symbol && token.decimals !== undefined;
    }
  });
};

Completeness filtering ensures your users have meaningful information about the tokens they’re viewing.