Skip to main content
Your bot finds a 10x opportunity—a new token trending on social with an early chart that looks perfect. You buy in automatically. When you try to take profit, nothing happens—there’s a 95% sell tax. The token is a honeypot designed to trap bots. This guide shows you how to filter the noise and protect your capital.

Why Bots Need Risk Screening

Speed + Safety

Sub-second screening that doesn’t slow down execution

Honeypot Detection

Catch traps before capital gets locked

Sniper Detection

Know when you’re competing against coordinated actors
Why trading bot developers choose Webacy:
  • Trading Lite API — Sub-200ms token screening optimized for bots
  • Honeypot detection — Don’t buy what you can’t sell
  • Hidden tax detection — Expose the real cost before buying
  • Holder analysis — Detect sniper clusters and bundler activity
  • Liquidity analysis — Verify there’s enough depth to exit
  • Multi-chain support — Solana, Base, ETH, and more

Prerequisites

Before implementing risk screening, ensure you have:
  • A Webacy API key (sign up here)
  • Basic familiarity with REST APIs or the Webacy SDK
  • Your bot’s trade execution flow identified for integration points

Fast Token Screening

Speed is critical for trading bots. The Trading Lite API is optimized for sub-second decisions.

Trading Lite API (Solana)

Get essential risk data in under 200ms.
Trading Lite is currently available for Solana only. For EVM chains, use the standard contract analysis endpoints.
curl -X GET "https://api.webacy.com/trading-lite/TokenMintAddress..." \
  -H "x-api-key: YOUR_API_KEY"
Response fields:
FieldTypeDescription
riskScorenumber0-100 overall risk score
isHoneypotbooleanCan you sell this token?
liquiditynumberTotal liquidity in USD
holderCountnumberNumber of token holders

Honeypot Detection

The most critical check—don’t buy what you can’t sell.
interface TradeDecision {
  action: 'buy' | 'skip';
  reason: string;
  riskScore: number;
}

async function evaluateToken(mint: string): Promise<TradeDecision> {
  const analysis = await client.tradingLite.analyze(mint);

  // Immediate rejection: honeypot
  if (analysis.isHoneypot) {
    return {
      action: 'skip',
      reason: 'Honeypot detected - cannot sell',
      riskScore: 100,
    };
  }

  // Risk score threshold
  if (analysis.riskScore > 60) {
    return {
      action: 'skip',
      reason: `High risk score: ${analysis.riskScore}`,
      riskScore: analysis.riskScore,
    };
  }

  // Liquidity threshold
  if (analysis.liquidity < 5000) {
    return {
      action: 'skip',
      reason: `Insufficient liquidity: $${analysis.liquidity}`,
      riskScore: analysis.riskScore,
    };
  }

  return {
    action: 'buy',
    reason: 'Passed all checks',
    riskScore: analysis.riskScore,
  };
}

Tax Detection

Hidden taxes can destroy profitability. A 10x gain means nothing with a 90% sell tax.
curl -X GET "https://api.webacy.com/contracts/0xTokenAddress.../tax?chain=eth" \
  -H "x-api-key: YOUR_API_KEY"
Tax thresholds for bots:
ScenarioMax TaxReasoning
Scalping3% totalNeed tight spreads
Swing trading10% totalCan absorb some tax
Long-term15% totalPrice appreciation covers tax
Never trade> 25%Likely scam

Sniper & Bundler Detection

Know what you’re up against. If coordinated actors are already in, you’re probably late.

Holder Analysis

curl -X GET "https://api.webacy.com/holder-analysis/TokenMint...?chain=sol" \
  -H "x-api-key: YOUR_API_KEY"

Launch Screening

For new token launches, check the first buyers.
interface LaunchAnalysis {
  safeToEnter: boolean;
  sniperRisk: 'low' | 'medium' | 'high';
  bundlerRisk: 'low' | 'medium' | 'high';
  concentrationRisk: 'low' | 'medium' | 'high';
  reasons: string[];
}

async function analyzeLaunch(mint: string): Promise<LaunchAnalysis> {
  const holders = await client.holderAnalysis.get(mint, { chain: Chain.SOL });
  const reasons: string[] = [];

  // Evaluate sniper risk
  let sniperRisk: 'low' | 'medium' | 'high' = 'low';
  if (holders.sniperCount > 10) {
    sniperRisk = 'high';
    reasons.push(`${holders.sniperCount} snipers detected`);
  } else if (holders.sniperCount > 5) {
    sniperRisk = 'medium';
  }

  // Evaluate bundler risk
  let bundlerRisk: 'low' | 'medium' | 'high' = 'low';
  if (holders.bundlerCount > 3) {
    bundlerRisk = 'high';
    reasons.push(`${holders.bundlerCount} bundler clusters`);
  } else if (holders.bundlerCount > 1) {
    bundlerRisk = 'medium';
  }

  // Evaluate concentration risk
  let concentrationRisk: 'low' | 'medium' | 'high' = 'low';
  if (holders.top10Percentage > 80) {
    concentrationRisk = 'high';
    reasons.push(`Top 10 hold ${holders.top10Percentage}%`);
  } else if (holders.top10Percentage > 60) {
    concentrationRisk = 'medium';
  }

  // Overall decision
  const highRisks = [sniperRisk, bundlerRisk, concentrationRisk]
    .filter(r => r === 'high').length;

  const safeToEnter = highRisks === 0;

  return {
    safeToEnter,
    sniperRisk,
    bundlerRisk,
    concentrationRisk,
    reasons,
  };
}

Liquidity Checks

Ensure you can exit your position.

Pool Analysis

curl -X GET "https://api.webacy.com/tokens/TokenAddress.../pools?chain=sol" \
  -H "x-api-key: YOUR_API_KEY"

Slippage Estimation

function estimateSlippage(positionSize: number, liquidity: number): number {
  // Simple constant product formula approximation
  // Real slippage depends on pool type and depth distribution
  const impactRatio = positionSize / liquidity;
  const slippage = impactRatio * 100 * 2; // 2x for round trip

  return Math.min(slippage, 100); // Cap at 100%
}

// Usage
const liquidity = 50000; // $50k in pool
const position = 5000;   // $5k trade

const expectedSlippage = estimateSlippage(position, liquidity);
console.log(`Expected slippage: ~${expectedSlippage.toFixed(2)}%`);

// Decision threshold
if (expectedSlippage > 5) {
  console.warn('⚠️ High slippage expected - reduce position size');
}

Complete Integration Workflow

Bot Decision Flow

Full TypeScript Implementation

import { TradingClient, ThreatClient, Chain } from '@webacy-xyz/sdk';

const tradingClient = new TradingClient({
  apiKey: process.env.WEBACY_API_KEY,
  defaultChain: Chain.SOL,
});

const threatClient = new ThreatClient({
  apiKey: process.env.WEBACY_API_KEY,
});

interface RiskEngineResult {
  shouldTrade: boolean;
  maxPositionUsd: number;
  riskScore: number;
  flags: string[];
  metrics: {
    liquidity: number;
    buyTax: number;
    sellTax: number;
    sniperCount: number;
    bundlerCount: number;
    top10Concentration: number;
  };
}

// Main risk engine function
async function evaluateToken(
  tokenAddress: string,
  chain: Chain,
  intendedPositionUsd: number
): Promise<RiskEngineResult> {
  const flags: string[] = [];

  // Step 1: Quick check (Trading Lite for Solana, contract analysis for EVM)
  let quickCheck: { riskScore: number; isHoneypot: boolean; liquidity: number };

  if (chain === Chain.SOL) {
    quickCheck = await tradingClient.tradingLite.analyze(tokenAddress);
  } else {
    // EVM chains - use contract analysis
    const contract = await threatClient.contracts.analyze(tokenAddress, { chain });
    const pools = await tradingClient.tokens.getPools(tokenAddress, { chain });
    const totalLiquidity = pools.pools.reduce((sum, p) => sum + p.liquidity, 0);

    quickCheck = {
      riskScore: contract.overallRisk,
      isHoneypot: contract.isHoneypot ?? false,
      liquidity: totalLiquidity,
    };
  }

  // Immediate rejection: honeypot
  if (quickCheck.isHoneypot) {
    return {
      shouldTrade: false,
      maxPositionUsd: 0,
      riskScore: 100,
      flags: ['HONEYPOT DETECTED'],
      metrics: {
        liquidity: quickCheck.liquidity,
        buyTax: 0,
        sellTax: 100,
        sniperCount: 0,
        bundlerCount: 0,
        top10Concentration: 0,
      },
    };
  }

  // Step 2: Tax analysis (EVM only)
  let buyTax = 0;
  let sellTax = 0;

  if (chain !== Chain.SOL) {
    try {
      const tax = await threatClient.contracts.getTax(tokenAddress, { chain });
      buyTax = tax.buyTax;
      sellTax = tax.sellTax;

      if (sellTax > 25) {
        flags.push(`EXTREME SELL TAX: ${sellTax}%`);
      } else if (sellTax > 10) {
        flags.push(`High sell tax: ${sellTax}%`);
      }

      if (buyTax > 10) {
        flags.push(`High buy tax: ${buyTax}%`);
      }
    } catch {
      // Tax check failed - proceed with caution
      flags.push('Tax check unavailable');
    }
  }

  // Step 3: Holder analysis
  let sniperCount = 0;
  let bundlerCount = 0;
  let top10Concentration = 0;

  try {
    const holders = await tradingClient.holderAnalysis.get(tokenAddress, { chain });
    sniperCount = holders.sniperCount;
    bundlerCount = holders.bundlerCount;
    top10Concentration = holders.top10Percentage;

    if (sniperCount > 10) {
      flags.push(`Heavy sniping: ${sniperCount} snipers`);
    }

    if (bundlerCount > 3) {
      flags.push(`Bundler activity: ${bundlerCount} clusters`);
    }

    if (top10Concentration > 80) {
      flags.push(`High concentration: Top 10 hold ${top10Concentration}%`);
    }
  } catch {
    flags.push('Holder analysis unavailable');
  }

  // Step 4: Calculate max position based on liquidity
  const liquidity = quickCheck.liquidity;
  const maxPositionUsd = liquidity * 0.02; // 2% of liquidity

  if (intendedPositionUsd > maxPositionUsd) {
    flags.push(`Position too large for liquidity`);
  }

  if (liquidity < 5000) {
    flags.push(`Low liquidity: $${liquidity.toLocaleString()}`);
  }

  // Final decision
  const criticalFlags = flags.filter(f =>
    f.includes('EXTREME') ||
    f.includes('HONEYPOT') ||
    f.includes('Position too large')
  );

  const shouldTrade =
    !quickCheck.isHoneypot &&
    quickCheck.riskScore < 60 &&
    sellTax < 25 &&
    liquidity >= intendedPositionUsd * 50 &&
    criticalFlags.length === 0;

  return {
    shouldTrade,
    maxPositionUsd,
    riskScore: quickCheck.riskScore,
    flags,
    metrics: {
      liquidity,
      buyTax,
      sellTax,
      sniperCount,
      bundlerCount,
      top10Concentration,
    },
  };
}

// Batch evaluation for multiple tokens
async function evaluateTokenBatch(
  tokens: Array<{ address: string; chain: Chain }>,
  positionSizeUsd: number
): Promise<Map<string, RiskEngineResult>> {
  const results = new Map<string, RiskEngineResult>();

  // Process in parallel (up to 10 at a time)
  const batchSize = 10;

  for (let i = 0; i < tokens.length; i += batchSize) {
    const batch = tokens.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(t => evaluateToken(t.address, t.chain, positionSizeUsd))
    );

    batch.forEach((token, index) => {
      results.set(token.address, batchResults[index]);
    });
  }

  return results;
}

// Pre-trade checklist
async function preTradeCheck(
  tokenAddress: string,
  chain: Chain,
  positionUsd: number
): Promise<{
  approved: boolean;
  checklist: Array<{ check: string; passed: boolean; details: string }>;
}> {
  const evaluation = await evaluateToken(tokenAddress, chain, positionUsd);

  const checklist = [
    {
      check: 'Not a honeypot',
      passed: evaluation.metrics.sellTax < 100,
      details: evaluation.metrics.sellTax >= 100 ? 'Cannot sell' : 'Sellable',
    },
    {
      check: 'Acceptable tax',
      passed: evaluation.metrics.sellTax < 15,
      details: `Buy: ${evaluation.metrics.buyTax}%, Sell: ${evaluation.metrics.sellTax}%`,
    },
    {
      check: 'Sufficient liquidity',
      passed: evaluation.metrics.liquidity >= positionUsd * 50,
      details: `$${evaluation.metrics.liquidity.toLocaleString()} available`,
    },
    {
      check: 'Not heavily sniped',
      passed: evaluation.metrics.sniperCount < 10,
      details: `${evaluation.metrics.sniperCount} snipers detected`,
    },
    {
      check: 'Not concentrated',
      passed: evaluation.metrics.top10Concentration < 80,
      details: `Top 10 hold ${evaluation.metrics.top10Concentration}%`,
    },
    {
      check: 'Risk score acceptable',
      passed: evaluation.riskScore < 60,
      details: `Score: ${evaluation.riskScore}/100`,
    },
  ];

  return {
    approved: checklist.every(c => c.passed),
    checklist,
  };
}

// Usage example
async function main() {
  const token = 'PumpTokenMint...';
  const positionSize = 500; // $500

  const check = await preTradeCheck(token, Chain.SOL, positionSize);

  console.log('\nPre-Trade Checklist:');
  console.log('==================');

  for (const item of check.checklist) {
    const status = item.passed ? '✓' : '✗';
    console.log(`${status} ${item.check}: ${item.details}`);
  }

  console.log('==================');
  console.log(`Decision: ${check.approved ? 'APPROVED' : 'REJECTED'}`);
}

Example Tokens for Testing

Solana Tokens

Test against tokens from Pump.fun, Raydium, and Jupiter trending lists. Scam tokens are deployed constantly, so live data is the best test data.

EVM Test Tokens

AddressChainDescription
0xdAC17F958D2ee523a2206206994597C13D831ec7ETHUSDT - safe, no tax
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48ETHUSDC - safe baseline
Testing tip: For realistic testing, grab tokens from live trending feeds and run them through your risk engine. This gives you real data on current scam patterns.

Performance Optimization

Caching Strategy

// Cache results to reduce API calls
const cache = new Map<string, { result: RiskEngineResult; timestamp: number }>();
const CACHE_TTL = 60 * 1000; // 1 minute

async function evaluateWithCache(
  tokenAddress: string,
  chain: Chain,
  positionUsd: number
): Promise<RiskEngineResult> {
  const cacheKey = `${tokenAddress}-${chain}`;
  const cached = cache.get(cacheKey);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.result;
  }

  const result = await evaluateToken(tokenAddress, chain, positionUsd);
  cache.set(cacheKey, { result, timestamp: Date.now() });

  return result;
}

Parallel Processing

// Evaluate multiple tokens in parallel
async function batchEvaluate(
  tokens: string[],
  chain: Chain,
  positionUsd: number
): Promise<Map<string, RiskEngineResult>> {
  const results = await Promise.all(
    tokens.map(t => evaluateToken(t, chain, positionUsd))
  );

  return new Map(tokens.map((t, i) => [t, results[i]]));
}

API Quick Reference

EndpointUse CaseResponse Time
GET /trading-lite/{address}Quick analysis (SOL)~200ms
GET /contracts/{address}Contract analysis~500ms
GET /contracts/{address}/taxTax detection~300ms
GET /holder-analysis/{address}Holder distribution~400ms
GET /tokens/{address}/poolsLiquidity pools~300ms

Next Steps