'Optimize Alchemy SDK performance with caching, batching, and multi-chain
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill alchemy-performance-tuning --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Alchemy Performance Tuning?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-alchemy-performance-tuning-tons-of-skills-marketplace)More formats (shields.io, HTML) on the badges page.
---
name: alchemy-performance-tuning
description: 'Optimize Alchemy SDK performance with caching, batching, and multi-chain
parallelism.
Use when reducing latency for blockchain queries, optimizing CU consumption,
or scaling dApps for high request volumes.
Trigger: "alchemy performance", "alchemy slow", "alchemy optimization",
"alchemy caching", "alchemy batch requests".
'
allowed-tools: Read, Write, Edit, Bash(npm:*)
version: 1.5.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- blockchain
- web3
- alchemy
- performance
compatibility: Designed for Claude Code
---
# Alchemy Performance Tuning
## 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
| Operation | Target Latency | CU Cost |
|-----------|---------------|---------|
| `getBlockNumber` | < 50ms | 10 |
| `getBalance` | < 100ms | 19 |
| `getTokenBalances` | < 200ms | 50 |
| `getNftsForOwner` | < 300ms | 50 |
| `getAssetTransfers` | < 500ms | 150 |
| 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
```typescript
// 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
```typescript
// 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)
```typescript
// 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
```typescript
// 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
| Failure | Response |
|---------|----------|
| Cache entry exceeds its freshness policy | Invalidate it and refresh from the provider before rendering a result. |
| One chain query fails | Preserve successful-chain results and surface an explicit unavailable state for the failed chain. |
| WebSocket repeatedly disconnects | Use bounded reconnect backoff, alert on sustained failure, and fall back to rate-limited polling. |
| Batch call partially fails | Keep successful results, retry only eligible failed items, and respect the provider limit. |
## Resources
- [Alchemy Compute Units](https://www.alchemy.com/docs/reference/compute-unit-costs)
- Alchemy WebSockets
## Next Steps
For cost optimization, see `alchemy-cost-tuning`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!