@blockrun/nano-client
TypeScript SDK for nano.blockrun.ai — pay-per-request access to 80+ AI models (GPT-5.x, Claude 4.x, Gemini 3.x, DeepSeek, Grok, NVIDIA free-tier, GLM, MiniMax, Moonshot…) plus image / video / music / search / X-Twitter intelligence / Pyth-backed market data — all settled with gas-free batched USDC via Circle Gateway. No API keys required; your wallet signature is your authentication. Built for AI agents that need to operate autonomously across Polygon / Arbitrum / Optimism / Unichain mainnet.

Sister SDKs: blockrun-llm (Python, Base / Solana) · this package = mirror for Circle Gateway batched payments on multi-chain EVM.
Why nano
| Buyer chain options | Base only | Polygon / Arbitrum / Optimism / Unichain |
| First call setup | Have USDC + ETH on Base | Deposit once into Circle Gateway (one tx) |
| Per-call gas (buyer) | Yes (~$0.01–0.05) | Zero (off-chain EIP-712 signature) |
| Settlement | Synchronous on-chain | Batched ~every 15 min on Polygon |
| Best for | One-off calls | High-frequency AI agents, long-running sessions |
Supported chains
All 10 mainnet chains Circle Gateway supports today (Base excluded by design):
| Polygon | polygon | 137 | POL |
| Arbitrum | arbitrum | 42161 | ETH |
| OP Mainnet | optimism | 10 | ETH |
| Unichain | unichain | 130 | ETH |
| Avalanche C-Chain | avalanche | 43114 | AVAX |
| Sonic | sonic | 146 | S |
| Sei EVM | sei | 1329 | SEI |
| WorldChain | worldChain | 480 | ETH |
| HyperEVM | hyperEvm | 999 | HYPE |
| Ethereum | ethereum | 1 | ETH |
Base is intentionally not in nano — buyers on Base should use blockrun.ai (native x402, no Gateway deposit step required).
Install
npm install @blockrun/nano-client
pnpm add @blockrun/nano-client
Quick start
import { NanoClient } from "@blockrun/nano-client";
const client = new NanoClient({
chain: "polygon",
privateKey: process.env.PRIVATE_KEY as `0x${string}`,
});
await client.deposit("5");
const r = await client.chat({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(r.data.choices[0].message.content);
console.log(r.payment.formattedAmount);
For an even-shorter shape:
const reply = await client.ask("openai/gpt-4o-mini", "What is 2+2?");
console.log(reply);
Configuration
const client = new NanoClient({
privateKey: "0x...",
chain: "polygon",
baseUrl: "https://nano.blockrun.ai",
rpcUrl: "https://polygon-mainnet.g.alchemy.com/v2/KEY",
maxRetries: 2,
});
If rpcUrl is omitted, the SDK uses a vetted public RPC per chain from
RECOMMENDED_RPC_URLS (Polygon → 1rpc.io, Arb/Op → llamarpc, Unichain →
drpc). Override for production.
The default baseUrl is https://nano.blockrun.ai. A NANO_MAINNET_DIRECT_URL
constant is also exported for fallback during DNS / CDN incidents.
Chat
OpenAI-compatible chat
const r = await client.chat({
model: "anthropic/claude-haiku-4.5",
messages: [
{ role: "system", content: "You answer in one sentence." },
{ role: "user", content: "Explain MEV." },
],
max_tokens: 200,
});
Simple ask(model, prompt)
const reply = await client.ask("openai/gpt-4o-mini", "List 3 EVM rollups");
Smart routing (ClawRouter)
Let nano pick the cheapest capable model based on a 14-dimension classifier:
const r = await client.smartChat({ prompt: "What is 2+2?" });
console.log(r.data.model);
const hard = await client.smartChat({
prompt: "Prove the Riemann hypothesis step by step",
routing_profile: "premium",
});
console.log(hard.data.model);
"free" | NVIDIA free-tier models only (zero cost) |
"eco" | Cheapest capable model per tier (DeepSeek, NVIDIA) |
"auto" (default) | Best balance of cost / quality |
"premium" | Top-tier (OpenAI, Anthropic) |
Image generation
const r = await client.images.generate({
model: "openai/dall-e-3",
prompt: "A cat coding TypeScript at sunset, isometric voxel art",
size: "1024x1024",
});
console.log(r.data);
Image-to-image edit:
const r = await client.images.edit({
model: "google/nano-banana",
prompt: "Make the sky purple",
image: "data:image/png;base64,…",
});
Video & music
const job = await client.videos.generate({
model: "minimax/video-01",
prompt: "A red apple slowly rotating on a wooden table",
duration: 6,
});
const status = await client.videos.status(job.data.id);
const m = await client.music.generate({
prompt: "Lo-fi hip hop, soft piano, rainy night",
duration: 30,
});
Search
const r = await client.search({ query: "latest Solana TVL changes this week" });
Powered by AttentionVC. All methods return { data, payment } — typed
generic so you can pass your own response shape.
const profile = await client.x.userInfo("vitalikbuterin");
const followers = await client.x.followers("vitalikbuterin", { cursor: "..." });
const search = await client.x.search("blockrun OR x402", { query_type: "Latest" });
const trending = await client.x.trending();
const tweet = await client.x.tweetLookup("1234567890123456789");
const thread = await client.x.tweetThread("1234567890123456789");
const mentions = await client.x.userMentions("vitalikbuterin");
const articles = await client.x.articlesRising();
Full method list:
client.x.userLookup(usernames) | Profile lookup, single or batch |
client.x.userInfo(username) | Single profile + intel layer |
client.x.followers(username, {cursor?}) | $0.05/page (~200 accounts) |
client.x.followings(username, {cursor?}) | |
client.x.verifiedFollowers(username, {cursor?}) | |
client.x.userTweets(username, {cursor?, limit?}) | |
client.x.userMentions(username, {cursor?, limit?}) | |
client.x.tweetLookup(tweetIds) | Single or batch |
client.x.tweetReplies(tweetId, {cursor?}) | |
client.x.tweetThread(tweetId, {cursor?}) | |
client.x.search(query, {query_type?, cursor?}) | $0.032/page |
client.x.trending() | |
client.x.articlesRising() | |
Pyth-backed price + Predexon markets
const px = await client.price.price("BTC/USD");
const hist = await client.price.history("ETH/USD", { range: "7d", resolution: "1h" });
const market = await client.price.pm("polymarket", { id: "..." });
Wallet & Gateway management
client.address;
await client.getBalances();
await client.deposit("5");
await client.withdraw("2");
await client.withdraw("2", { chain: "base" });
Where do my paid funds end up? (seller-side)
When a payment intent reaches Completed, funds land in the seller's
Circle Gateway available balance — not directly in their wallet. The
seller mints to their wallet on whichever chain they want.
Query a seller's Gateway balance — read-only, no private key:
import { querySellerGatewayBalance } from "@blockrun/nano-client";
const b = await querySellerGatewayBalance(
"0xe9030014F5DAe217d0A152f02A043567b16c1aBf",
"polygon",
);
console.log(b.available);
To mint to wallet (seller-side, requires seller's private key):
const seller = new NanoClient({ chain: "polygon", privateKey: SELLER_KEY });
await seller.withdraw("5");
await seller.withdraw("5", { chain: "base" });
Payment intent tracking
Every paid call returns payment.transaction — Circle's nanopayment intent UUID.
const r = await client.chat({ ... });
const status = await client.getPaymentStatus(r.payment.transaction);
const final = await client.waitForSettlement(r.payment.transaction, {
timeoutMs: 5 * 60_000,
pollIntervalMs: 10_000,
});
Status flow (per Circle's docs):
Received | Verified, queued for next batch |
Batched | On-chain batching in progress |
Confirmed | Batch tx submitted on-chain |
Completed | Batch finalised → seller's Circle Gateway balance increased |
Spending tracker
Every paid call increments an in-process counter:
const s = client.getSpending();
console.log(`Spent $${s.total_usd.toFixed(4)} across ${s.calls} calls`);
console.log(s.by_endpoint);
client.resetSpending();
Generic raw call
For endpoints not covered by typed helpers:
const r = await client.call("/api/v1/audio/generations", {
method: "POST",
body: { model: "openai/tts-1", voice: "alloy", input: "Hello" },
});
End-to-end example
git clone https://github.com/BlockRunAI/blockrun-nano-client
cd blockrun-nano-client
pnpm install
pnpm exec tsx examples/print-address.ts
CLIENT_PRIVATE_KEY=0x... pnpm exec tsx examples/e2e-test.ts
How it works
- Buyer deposits USDC once into Circle's
GatewayWallet contract on chosen chain
- Every API call returns
402 Payment Required with a multi-chain accepts array
- SDK signs an EIP-712
TransferWithAuthorization against GatewayWallet
- Server forwards to Circle's facilitator → Circle queues for batch
- Circle batches every ~15 min on Polygon; pushes USDC into seller's Gateway balance
- Seller
withdraw()s to wallet (instant, any Gateway-supported chain)
Your private key never leaves your machine. The SDK signs locally, only the signature is sent.
Production tips
- Key management — KMS / Vault, not raw
.env
- RPC — provide your own Alchemy / QuickNode key for production traffic
- Balance monitoring —
await client.getBalances() and alert when gateway.available drops below your threshold
- Retries — built-in
maxRetries: 2 with exponential backoff handles transient 5xx; bump for high-volume agents
- Reconciliation — trust
client.getBalances() per chain over reading the chain yourself
Links
License
Apache-2.0