Skip to content

Rate Limits

Reference for rate limiting in BNB-Chain-MCP and integrated external APIs.


Overview

Rate limits protect both the MCP server and external API providers from abuse. Understanding these limits helps you build robust applications.


MCP Server Limits

Default Limits

Mode Requests/Minute Concurrent Burst
stdio 100 5 20
HTTP 60 10 15

Configuration

Set custom limits via environment variables:

RATE_LIMIT_RPM=200          # Requests per minute
RATE_LIMIT_CONCURRENT=10    # Concurrent requests
RATE_LIMIT_BURST=30         # Burst allowance

External API Limits

CoinGecko

Tier Requests/Minute Daily Limit
Free 10-30 10,000
Demo 30 10,000
Pro 500 Unlimited

Affected Tools: - market_get_price - market_get_ohlcv - market_get_trending - market_get_global_data - market_get_fear_greed_index

CoinStats

Tier Requests/Minute Monthly Limit
Free 5 5,000
Pro 30 100,000

Affected Tools: - market_coinstats_* tools

GoPlus Security

Tier Requests/Minute Daily Limit
Free 20 1,000
Pro 100 50,000

Affected Tools: - security_check_token - security_check_address - security_check_approval - security_check_nft

DefiLlama

Access Requests/Minute Notes
Public 5 No auth required

Affected Tools: - defi_get_protocol_tvl - defi_get_chain_tvl - defi_get_yields - defi_get_stablecoin_data

GeckoTerminal / DexPaprika

API Requests/Minute Notes
GeckoTerminal 30 Free tier
DexPaprika 30 Free tier

Affected Tools: - dex_get_pool_info - dex_get_trades - dex_get_ohlcv

Blockchain RPC

Network Requests/Minute Notes
Public RPC 10-50 Varies by provider
Private RPC 100-1000+ Depends on plan

Affected Tools: - All blockchain read/write operations


Rate Limit Headers

HTTP mode returns rate limit info in headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1710432000
Retry-After: 30

Error Response

When rate limited:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32600,
    "message": "Rate limit exceeded",
    "data": {
      "limit": 60,
      "window": "60s",
      "retryAfter": 30,
      "source": "coingecko"
    }
  }
}

Best Practices

1. Implement Exponential Backoff

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error: any) {
      if (error.code === -32600 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}

2. Use Caching

const cache = new Map<string, { data: any; expires: number }>();

async function cachedCall(
  mcp: MCPClient,
  tool: string,
  args: Record<string, any>,
  ttlSeconds: number = 60
): Promise<any> {
  const key = `${tool}:${JSON.stringify(args)}`;
  const cached = cache.get(key);

  if (cached && cached.expires > Date.now()) {
    return cached.data;
  }

  const data = await mcp.callTool(tool, args);
  cache.set(key, {
    data,
    expires: Date.now() + ttlSeconds * 1000
  });

  return data;
}

3. Batch Requests

Instead of multiple individual calls:

// Bad - 10 requests
for (const token of tokens) {
  await mcp.callTool('market_get_price', { coinId: token });
}

// Good - 1 request
await mcp.callTool('market_get_prices', { 
  coinIds: tokens.join(',') 
});

4. Use Rate Limiter

import Bottleneck from 'bottleneck';

const limiter = new Bottleneck({
  minTime: 100,        // 100ms between requests
  maxConcurrent: 5,    // Max 5 concurrent
  reservoir: 60,       // 60 requests
  reservoirRefreshAmount: 60,
  reservoirRefreshInterval: 60 * 1000  // Per minute
});

async function rateLimitedCall(mcp: MCPClient, tool: string, args: any) {
  return limiter.schedule(() => mcp.callTool(tool, args));
}

5. Prioritize Requests

const highPriorityLimiter = new Bottleneck({ maxConcurrent: 3 });
const lowPriorityLimiter = new Bottleneck({ maxConcurrent: 1 });

// Critical operations (trades, security)
async function highPriority(fn: () => Promise<any>) {
  return highPriorityLimiter.schedule(fn);
}

// Non-critical (market data, analytics)
async function lowPriority(fn: () => Promise<any>) {
  return lowPriorityLimiter.schedule(fn);
}

Tool-Specific Recommendations

High-Frequency Tools

These tools are commonly called frequently - use caching:

Tool Recommended TTL
market_get_price 30s
gas_get_gas_price 15s
network_get_block_number 5s

Low-Frequency Tools

These change rarely - cache longer:

Tool Recommended TTL
network_list_networks 24h
tokens_get_token_info 1h
security_check_token 5m

Real-Time Tools

Avoid caching these:

  • swap_get_quote - Prices change rapidly
  • lending_get_health_factor - Critical for safety
  • Transaction execution tools

Monitoring Rate Limits

Track Usage

class RateLimitMonitor {
  private usage: Map<string, number[]> = new Map();

  track(source: string) {
    const now = Date.now();
    const calls = this.usage.get(source) || [];

    // Keep last minute only
    const recent = calls.filter(t => now - t < 60000);
    recent.push(now);
    this.usage.set(source, recent);

    return recent.length;
  }

  getUsage(source: string): number {
    const calls = this.usage.get(source) || [];
    return calls.filter(t => Date.now() - t < 60000).length;
  }

  isNearLimit(source: string, limit: number): boolean {
    return this.getUsage(source) > limit * 0.8;
  }
}

Alert on Limits

monitor.on('nearLimit', (source, usage, limit) => {
  console.warn(`⚠️ ${source}: ${usage}/${limit} requests (80% threshold)`);
});

monitor.on('limited', (source) => {
  console.error(`🚫 ${source}: Rate limited!`);
});

API Key Configuration

Increase limits with API keys:

# CoinGecko Pro
COINGECKO_API_KEY=your-key

# GoPlus Pro
GOPLUS_API_KEY=your-key

# Custom RPC (higher limits)
ETHEREUM_RPC_URL=https://your-rpc.com
ARBITRUM_RPC_URL=https://your-arb-rpc.com