Alchemy Performance Tuning

SkillDev tools

Speeds up your agent's blockchain data lookups by adding caching, batching, and multi-chain support to Alchemy SDK calls.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Alchemy Performance Tuning skill

About this capability

Tune Alchemy-backed reads with measured latency, cache semantics, batching, concurrency, and freshness SLOs. Use when an integration is slow or wasteful. Trigger with "optimize Alchemy performance", "cache Alchemy data", or "reduce Alchemy latency".

What this skill tells your AI

The instructions your AI receives, as published by jeremylongshore/tons-of-skills-marketplace in skills/.curated/alchemy-performance-tuning/SKILL.md and read by ahel’s review.

Overview

Optimize a Web3 application's response time and provider use through freshness-aware caching, bounded parallelism, batching, and real-time subscriptions. Measure improvements against an approved baseline rather than assuming fewer calls always preserves correct chain state.

Performance Targets

OperationTarget LatencyCU Cost
getBlockNumber< 50ms10
getBalance< 100ms19
getTokenBalances< 200ms50
getNftsForOwner< 300ms50
getAssetTransfers< 500ms150
Multi-chain portfolio< 2s~400

Prerequisites

  • A representative, non-sensitive benchmark workload with baseline latency, cache-hit, error-rate, and compute-unit measurements.
  • An explicit freshness policy for balances, blocks, transfers, ownership, and metadata, approved by the product owner.
  • Monitoring and a rollback flag that can disable cache, batching, or WebSocket changes if correctness or provider behavior regresses.

Instructions

Step 1: Response Caching with TTL

// src/performance/cache.ts
import { Alchemy, Network } from 'alchemy-sdk';

class BlockchainCache {
  private store = new Map<string, { data: any; expiry: number }>();

  // Different TTLs for different data freshness needs
  private TTL: Record<string, number> = {
    blockNumber: 12000,     // 12s (~1 block)
    balance: 30000,         // 30s
    tokenBalances: 60000,   // 60s
    nftOwnership: 300000,   // 5 min (NFTs transfer less frequently)
    contractMetadata: 3600000, // 1 hour (rarely changes)
    tokenMetadata: 86400000,   // 24 hours (almost never changes)
  };

  async cached<T>(category: string, key: string, fetcher: () => Promise<T>): Promise<T> {
    const cacheKey = `${category}:${key}`;
    const entry = this.store.get(cacheKey);
    if (entry && entry.expiry > Date.now()) return entry.data;

    const data = await fetcher();
    this.store.set(cacheKey, { data, expiry: Date.now() + (this.TTL[category] || 30000) });
    return data;
  }

  invalidate(category: string): void {
    for (const key of this.store.keys()) {
      if (key.startsWith(`${category}:`)) this.store.delete(key);
    }
  }
}

const cache = new BlockchainCache();
export { cache };

Step 2: Parallel Multi-Chain Fetching

// src/performance/parallel-fetch.ts
import { Alchemy, Network } from 'alchemy-sdk';
import { cache } from './cache';

const CHAINS = [
  { name: 'ethereum', network: Network.ETH_MAINNET },
  { name: 'polygon', network: Network.MATIC_MAINNET },
  { name: 'arbitrum', network: Network.ARB_MAINNET },
  { name: 'base', network: Network.BASE_MAINNET },
];

async function multiChainBalance(address: string) {
  const results = await Promise.allSettled(
    CHAINS.map(chain =>
      cache.cached('balance', `${chain.name}:${address}`, async () => {
        const client = new Alchemy({ apiKey: process.env.ALCHEMY_API_KEY, network: chain.network });
        const bal = await client.core.getBalance(address);
        return { chain: chain.name, balance: (parseInt(bal.toString()) / 1e18).toFixed(6) };
      })
    )
  );

  return results
    .filter((r): r is PromiseFulfilledResult<any> => r.status === 'fulfilled')
    .map(r => r.value);
}

Step 3: Batch NFT Metadata (Reduce CU)

// src/performance/batch-nft.ts
import { Alchemy, Network } from 'alchemy-sdk';

const alchemy = new Alchemy({ apiKey: process.env.ALCHEMY_API_KEY, network: Network.ETH_MAINNET });

// SLOW: Individual calls = 50 CU each
// async function slowGetMetadata(tokens) {
//   return Promise.all(tokens.map(t => alchemy.nft.getNftMetadata(t.contract, t.tokenId)));
// }

// FAST: Batch call = 50 CU total for up to 100 tokens
async function fastGetMetadata(tokens: Array<{ contractAddress: string; tokenId: string }>) {
  return alchemy.nft.getNftMetadataBatch(tokens);
}

Step 4: WebSocket for Real-Time Data

// src/performance/realtime.ts
import { Alchemy, AlchemySubscription, Network } from 'alchemy-sdk';

const alchemy = new Alchemy({ apiKey: process.env.ALCHEMY_API_KEY, network: Network.ETH_MAINNET });

// Use WebSocket subscriptions instead of polling
function watchAddress(address: string, onActivity: (tx: any) => void) {
  alchemy.ws.on(
    {
      method: AlchemySubscription.PENDING_TRANSACTIONS,
      toAddress: address,
    },
    (tx) => onActivity(tx)
  );
}

// Auto-reconnect on disconnect
alchemy.ws.on('close', () => {
  console.log('WebSocket disconnected — reconnecting in 5s');
  setTimeout(() => alchemy.ws.connect(), 5000);
});

Output

  • TTL-based response cache matching data freshness requirements
  • Parallel multi-chain fetching (4 chains in < 2s)
  • Batch NFT metadata (100x CU reduction)
  • WebSocket subscriptions replacing polling

Examples

Benchmark a public test address across the four listed networks before and after enabling the balance cache. Confirm the cached run reduces provider calls while its displayed data never exceeds the approved 30-second freshness window, and verify a single failed chain remains visibly unavailable rather than silently omitted. Next, send a small synthetic NFT list through the batch path and compare response count with individual calls. If cache age, error rate, or WebSocket reconnect behavior violates the defined threshold, disable that optimization using the rollback flag and investigate from aggregate metrics.

Error Handling

FailureResponse
Cache entry exceeds its freshness policyInvalidate it and refresh from the provider before rendering a result.
One chain query failsPreserve successful-chain results and surface an explicit unavailable state for the failed chain.
WebSocket repeatedly disconnectsUse bounded reconnect backoff, alert on sustained failure, and fall back to rate-limited polling.
Batch call partially failsKeep successful results, retry only eligible failed items, and respect the provider limit.

Resources

Next Steps

For cost optimization, see alchemy-cost-tuning.

Signals

GitHub stars
3k
Forks
396
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
alchemy-performance-tuning
Source
github.com/jeremylongshore/tons-of-skills-marketplace