@blockrun/llm
v3.24.0
Published
TypeScript SDK for BlockRun - every frontier model behind one OpenAI-compatible client. Pay with a BlockRun API key or per-call in USDC. Smart routing picks the cheapest capable model. No rate limits.
Downloads
11,609
Readme
@blockrun/llm
Cut your LLM bill by 84%. One line of TypeScript.
The smart-routing SDK for 88 models — every request goes to the cheapest model that can handle it, paid with an API key or per-request USDC on Solana, Base or Arc. No vendor lock-in.
Sign up / Get an API key · Website · Models & Pricing · ClawRouter · Python SDK · Telegram
import { LLMClient } from '@blockrun/llm';
// Reads BLOCKRUN_API_KEY (sign up at https://user.blockrun.ai),
// or BASE_CHAIN_WALLET_KEY if you'd rather pay per request in USDC.
const client = new LLMClient();
const r = await client.smartChat('Prove step by step that the sum of two odd integers is even.');
console.log(r.model); // 'deepseek/deepseek-v4-pro': a strong reasoner, not the frontier flagship
console.log(r.routing.savings); // e.g. 0.83: this request cost 83% less than the baseline
console.log(r.response); // the proof84% cheaper than pinning Claude Opus 5 across a realistic workload on the default auto profile, 98% on eco, which tries the free tier first. How that is measured: Smart Routing.
Why This SDK
- 🧠 Smart routing that pays for itself. The bundled Router Core V3 engine (shared with ClawRouter) classifies every request locally in <1ms across 15 dimensions and routes to the cheapest capable model.
- 🆓 7 genuinely free models. $0 in and out, including two 1M-context Nemotrons, two that accept images, and free coding models from Cohere and Poolside.
- 🔐 Two ways to connect. A BlockRun API key billed against account credit (sign up, create a key, add credit), or a wallet signature with x402 micropayments and no account at all. Same code either way.
- 💸 Pay per request in USDC. x402 micropayments on Solana, Base or Arc. Agents can fund and pay their own way.
- 🛡️ Automatic failover. Transient errors (timeouts, 429, 5xx) before payment walk the router's ranked fallback chain instead of failing your request; a failure after a payment was sent never buys a second model.
- ⚡ Streaming, OpenAI & Anthropic compat. Drop-in
chat.completions/messageslayers, SSE streaming, strict TypeScript. - 🎨 Beyond chat. Image, video, music, speech, live search, prediction markets, market data, and 40-chain RPC through the same API key or wallet.
How It Compares
| | OpenAI SDK | OpenRouter | LiteLLM | @blockrun/llm | | ------------------ | -------------- | ----------------- | ---------------- | ----------------------------------------------------------------------- | | Cost routing | ✗ one vendor | Manual selection | Manual selection | Automatic — 84% cheaper | | Models | GPT only | 200+ | 100+ (BYO keys) | 88, one credential | | Free tier | ✗ | Rate-limited | ✗ | 7 models, no signup | | Auth | API key | Account + API key | Your API keys | API key or wallet signature | | Payment | Card + invoice | Credit card | BYO keys | Account credit or USDC per-request | | Agent-ready | ✗ | ✗ | ✗ | ✓ — one key, or agents fund their own wallet |
Installation
npm install @blockrun/llm # Account API keys, Base and Arc wallets; smart routing includednpm install @blockrun/llm @solana/web3.js @solana/spl-tokenWhy they are not automatic: @solana/spl-token pulls in bigint-buffer, whose
native toBigIntLE() has an unpatched buffer overflow
(GHSA-3gc7-fjrx-p6mg) with no
fixed release anywhere. As an optional dependency it landed in the lockfile of
every consumer, including projects that only ever pay on Base. As an optional
peer it reaches only the projects that ask for Solana. Calling a Solana path
without them throws an error naming the exact install command.
Only relevant in wallet mode; an account API key is chain-independent.
| Chain | Network | Payment | Notes |
|-------|---------|---------|-------|
| Solana | Solana Mainnet | USDC (SPL) | Recommended for new wallets; SolanaLLMClient |
| Base | Base Mainnet (eip155:8453) | USDC | LLMClient; settled by the Coinbase CDP facilitator |
| Arc | Circle Arc (eip155:5042) | USDC | LLMClient with apiUrl: 'https://arc.blockrun.ai/api'; settled by Circle's facilitator |
| Base Sepolia | Base Sepolia (eip155:84532) | Testnet USDC | Development |
Protocol: x402 v2.
The Anthropic SDK is a runtime dependency because the public compatibility wrapper exposes its types. Solana signing dependencies remain optional and are unnecessary for account billing.
Documentation map
| If you want to… | Start here | |-----------------|------------| | Let the SDK choose the cheapest capable model | Smart Routing | | Try it for $0 | Try It Free and Free Tier | | Understand wallet payments and settlement | How Payment Works | | Pay from a Solana wallet | Solana Support | | Enable metered batch settlement | Batch settlement and the full reference | | Stream tokens, use tools | Chat, Tools and Streaming | | Generate media or query data services | Models and Service APIs | | Use the OpenAI or Anthropic SDK surface | OpenAI and Anthropic Compatibility | | Create, find or import a wallet | Wallets | | Configure production credentials safely | Configuration and Environment Variables and Security |
Quick Start
Choose one billing mode. The chat and routing APIs are the same in every mode.
| Mode | Credential | Settlement | Best for |
|------|------------|------------|----------|
| Account | BLOCKRUN_API_KEY | BlockRun account credit | Applications and teams that want the simplest setup |
| Solana wallet | SOLANA_WALLET_KEY | Per-request USDC over x402 | Agents and non-custodial workloads; recommended wallet chain |
| Base wallet | BASE_CHAIN_WALLET_KEY | Per-request USDC over x402 | Existing EVM wallets and Base-native applications (the same key pays on Arc) |
Credential precedence is deterministic. An explicit apiKey wins over the
environment, and an explicit privateKey selects wallet mode even when
BLOCKRUN_API_KEY is set; passing both explicitly is an error. With no explicit
credential, BLOCKRUN_API_KEY beats the wallet key env vars, so a process holding
both runs in account mode. An invalid or exhausted API key never falls back to a
wallet payment.
Account API key
- Sign up or sign in.
- Create a key on API Keys and add credit on Credits.
- Set the key and use any SDK client. No wallet or payment chain is required.
export BLOCKRUN_API_KEY=brk_live_...import { LLMClient, ImageClient, VideoClient, BlockrunClient } from '@blockrun/llm';
const llm = new LLMClient(); // reads BLOCKRUN_API_KEY
// Let the router pick the cheapest capable model...
const routed = await llm.smartChat('Explain x402 in one paragraph.');
console.log(routed.model, routed.response);
// ...or pin one yourself
console.log(await llm.chat('openai/gpt-5.2', 'Hello!'));
const images = new ImageClient({ apiKey: process.env.BLOCKRUN_API_KEY });
const videos = new VideoClient(); // same account; async jobs are polled for you
const api = new BlockrunClient(); // generic access, e.g. the Responses API
const response = await api.post('/v1/responses', { model: 'openai/gpt-5.2', input: 'Hello!' });Every client accepts apiKey: the service clients, SolanaLLMClient, and the
OpenAI/Anthropic compatibility wrappers. Usage and charges are on
Activity. Keep API keys in server
or local environment variables, never in browser code or logs. Base URL
overrides, retry and polling rules, and switching back to a wallet are under
Account mode details.
Solana wallet
import { setupAgentClient, SolanaLLMClient } from '@blockrun/llm';
// Uses BLOCKRUN_API_KEY when set. Otherwise it honours a saved chain preference,
// keeps an existing Base-only wallet on Base, and creates new wallets on Solana.
const client = await setupAgentClient();
const routed = await client.smartChat('Explain photosynthesis.');
console.log(routed.model, routed.response);
// Or construct the Solana client explicitly (needs the Solana peers from Installation above).
const solana = new SolanaLLMClient({ privateKey: process.env.SOLANA_WALLET_KEY });
console.log(await solana.chat('deepseek/deepseek-chat', 'gm')); // pin a modelBase wallet
import { LLMClient } from '@blockrun/llm';
const client = new LLMClient(); // BASE_CHAIN_WALLET_KEY; the key signs locally and is never sent
// Recommended: let the router pick the cheapest capable model
const result = await client.smartChat('Hello!');
console.log(result.model, result.response);
// Or pin a model yourself
const reply = await client.chat('openai/gpt-5.4', 'Hello!');That's it. The SDK handles the x402 payment, and smartChat() keeps the bill
down on every request. The router is bundled: no extra package to install.
To pay on Arc instead, point the same client at Arc's gateway; see
Arc Support.
Try It Free (No USDC Required)
Want to kick the tires before funding a wallet? Use BlockRun's free tier:
import { LLMClient } from '@blockrun/llm';
// The client still needs a wallet key or an API key to construct, but free
// models are answered without a payment: nothing is signed and nothing is charged.
const client = new LLMClient();
// Option 1: call a free model directly (guaranteed $0)
const reply = await client.chat('nvidia/nemotron-3.5-lightning', 'Explain x402 in 1 sentence');
// Option 2: let the router pick: 'eco' ranks the free tier first
const result = await client.smartChat('What is the capital of France?', { routingProfile: 'eco' });
console.log(result.model); // a free model, e.g. 'nvidia/nemotron-3.5-lightning'
console.log(result.response); // 'The capital of France is Paris.'
console.log(result.routing.savings); // 1 (100%)The seven free models are listed under Free Tier. eco lands on
a free model when one can handle the request and moves to the cheapest paid
model when none can; pin a free id when the call must cost $0.
Smart Routing (Router Core V3)
Let the SDK pick the cheapest capable model for each request: 84% cheaper than pinning Claude Opus 5 for the same traffic on auto, 98% on eco.
Not an "up to" figure. The baseline, the workload mix and the token ratio are
published in savings-mix.json,
priced against the live catalog, so anyone can recompute the claim and get the
same answer. The per-request routing.savings on each response is measured
against router-core's baseline model, anthropic/claude-opus-4.7, which is
priced the same as Claude Opus 5 ($5 input / $25 output per million tokens).
Smart routing is powered by the product-neutral
@blockrun/router-core V3 engine,
the same deterministic portfolio router that drives
ClawRouter. It is bundled into
this SDK: no separate router package to install, and the routing decision
runs locally with no extra model call.
Three ways to use it:
import { LLMClient, type ChatMessage, type Tool } from '@blockrun/llm';
const client = new LLMClient();
const messages: ChatMessage[] = [{ role: 'user', content: 'What is the weather in Paris?' }];
const tools: Tool[] = [{
type: 'function',
function: {
name: 'get_weather',
description: 'Current weather for a city',
parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
},
}];
// 1. smartChat(): one-line routed chat
const result = await client.smartChat('What is the capital of France?');
console.log(result.model); // 'google/gemini-2.5-flash'
console.log(`Saved ${(result.routing.savings * 100).toFixed(0)}%`); // 'Saved 90%': this request vs the baseline
// 2. smartChatCompletion(): full agent / tool conversations, routed
const agent = await client.smartChatCompletion(messages, { tools, toolChoice: 'auto' });
// 3. Model aliases: blockrun/auto | blockrun/eco | blockrun/premium
const reply = await client.chatCompletion('blockrun/auto', messages);
// Inspect a decision without paying for anything
const decision = await client.route('Prove the Riemann hypothesis step by step');
console.log(decision.model); // 'deepseek/deepseek-v4-pro'
console.log(decision.method); // 'portfolio'
console.log(decision.taskType); // 'reasoning'
console.log(decision.candidates); // ranked, capability-eligible models
console.log(decision.fallbacks); // ['google/gemini-3.5-flash', 'deepseek/deepseek-reasoner', 'xai/grok-4.3', ...]The aliases are resolved locally by chat() and chatCompletion() on both
LLMClient and SolanaLLMClient, by LLMClient.chatCompletionStream(), and by
the OpenAI-compatible wrapper. SolanaLLMClient.stream() and the
Anthropic-compatible wrapper (which proxies straight to the gateway's
/v1/messages) do not resolve them; pass a concrete model id there.
Example picks in this README reflect the live catalog when it was written. The router re-ranks as models and prices move, so your output can differ.
Automatic Fallback on Transient Errors
smartChat() populates a fallback chain from the portfolio ranking and
chat() / chatCompletion() walk it automatically when the primary model
returns a transient error — timeouts, network failures, 429 rate limits, or
5xx responses (502/503/504/522/524) — before any payment was sent. Other
4xx errors and PaymentError propagate immediately so wallet / auth issues
surface fast. (Solana's internal stale-blockhash re-sign is a separate,
lower-level retry inside the payment step — see How Payment Works.)
The next model is a new paid request, so the chain is only walked when the
failed request cannot have been charged. Every error from these calls carries
a retry disposition, which retryDisposition(err) returns:
'unpaid': nothing chargeable was sent — the unpaid first request and its 402 challenge, or signing the payment. A 429, 5xx, timeout or network error here moves on to the next model. On Solana, a503 PAYMENT_VERIFICATION_UNAVAILABLEin answer to the payment is'unpaid'too: the gateway sends it only when verification could not run, so nothing was settled or broadcast (the batch path already treats it as a refusal). This holds on every exact-paid Solana endpoint, not only chat.'paid-or-in-doubt': the signed payment (exact or batch) was sent and may have been charged — a timeout, abort or network error after sending it, any error status in answer to it, or a 2xx whose body could not be read. The error propagates; no other model is bought for the call. With an API key the request itself is billed, so only the account API's explicit answers are'unpaid': a 4xx (such as a 429), or a 5xx carrying its JSON error envelope (api.blockrun.ai debits only an accepted 2xx). An HTML or empty 5xx from a proxy, or a timeout, is not.
An error without a disposition counts as 'paid-or-in-doubt'. Use the same
check in your own retry wrapper, or pass your own fallback chain:
import { LLMClient, retryDisposition } from '@blockrun/llm';
const client = new LLMClient();
try {
await client.chat('openai/gpt-5.2', 'hello');
} catch (err) {
if (retryDisposition(err) !== 'unpaid') throw err; // may have been charged: never resend
// safe to retry: nothing was paid
}
// Manually pass a fallback chain to chat() / chatCompletion()
const reply = await client.chat('nvidia/nemotron-3.5-lightning', 'hello', {
fallbackModels: ['nvidia/nemotron-3-ultra-550b', 'cohere/north-mini-code'],
});
// If nemotron-3.5-lightning times out, the SDK retries against the next model
// and logs each hop to stderr: "[@blockrun/llm] <from> -> <to> (...)".Routing Profiles
| Profile | Strategy | Savings vs Opus 5 | Best For |
|---------|----------|-------------------|----------|
| eco | Cheapest capable model — ranks the 7-model free tier first | 98% | Cost-sensitive production, zero-cost testing |
| auto | Best balance of cost and quality (default) | 84% | General use |
| premium | Highest-quality model per tier | 0% | Quality-critical tasks |
There is no free routing profile: routingProfile accepts
'eco' | 'auto' | 'premium'. (ClawRouter's /model free belongs to its own
proxy, not to this SDK's router options.) For guaranteed $0, call a
free model directly with chat(); for smart-routed $0-first,
use eco.
import { LLMClient } from '@blockrun/llm';
const client = new LLMClient();
// Use premium models for quality-critical tasks
const result = await client.smartChat(
'Write production-grade async TypeScript code',
{ routingProfile: 'premium' }
);
console.log(result.model); // 'openai/gpt-5.3-codex'How the Router Works
flowchart LR
A["prompt"] --> B["classify locally<br/>15 dimensions, <1ms"]
B --> C["hard filters<br/>tools · vision · context ·<br/>structured output"]
C --> D["rank portfolio<br/>quality · cost · speed ·<br/>reliability"]
D --> E["cheapest capable model<br/>+ ranked fallback chain"]
E --> F["one request, paid by<br/>API key or x402 USDC"]
F --> G["response<br/>+ full routing metadata"]Auto uses the deterministic Router Core V3.5 portfolio strategy
(DEFAULT_ROUTING_CONFIG.version in the pinned engine): it classifies the task shape locally across
(token count, code presence, reasoning markers, technical/creative terms,
agentic patterns, …), enforces tool / vision / structured-output / context
constraints as hard filters, then ranks an ordered candidate portfolio.
The winner becomes routing.model; the rest surface as routing.candidates
and feed the transient-error fallback chain. Classification is local and
deterministic: <1ms, no extra model call. (The SDK reads /v1/models once to
price candidates; the catalog is cached.)
Classification still maps to one of four tiers (routing.tier). Each
tier × profile has a designated primary (what the rules strategy —
routing.method: 'rules', the rollback lever — routes to directly, and what
anchors the portfolio's candidate pool):
| Tier | Example Tasks | ECO | AUTO | PREMIUM | |------|---------------|-----|------|---------| | SIMPLE | "What is the capital of France?", definitions | nemotron-3.5-lightning (FREE) | gemini-2.5-flash | gemini-2.5-flash | | MEDIUM | Code snippets, explanations | glm-5.3-flash | gemini-3.5-flash | gpt-5.3-codex | | COMPLEX | Architecture, long documents | glm-5.3-flash | gemini-3.1-pro | claude-fable-5 | | REASONING | Proofs, multi-step reasoning | deepseek-reasoner | deepseek-reasoner | claude-sonnet-5 |
This table mirrors the pinned router-core DEFAULT_ROUTING_CONFIG;
routing.tierConfigs on any decision returns the map that decision used.
The router's ranking is used as-is. It can pick a model the gateway serves but
does not list on /v1/models (for example nvidia/nemotron-3-nano-30b, which
appears in eco fallback chains); the gateway serves those ids directly. The
only rewrite is ClawRouter's proxy namespace: free/<model> ids map to their
nvidia/<model> gateway ids, and are dropped when there is no mapping.
Routing Metadata Reference
Every smartChat() result carries the full decision on result.routing
(type RoutingDecision) — enough to log, audit, or replay why a model was
picked:
| Field | Description |
|-------|-------------|
| model | Selected model id (same as result.model) |
| method | 'portfolio' (the Auto default), 'rules' (rollback strategy), or 'llm' |
| tier | Task tier: 'SIMPLE', 'MEDIUM', 'COMPLEX', or 'REASONING' |
| taskType | Portfolio task classification: 'chat', 'extraction', 'code_edit', 'code_agent', 'tool_agent', 'debug', 'reasoning', 'reasoning_math', 'long_context', 'vision', … |
| candidates | Ordered, capability-eligible models ranked by the portfolio router; the first entry is model |
| candidateScores | Per-candidate score breakdown (quality / cost / speed / reliability), ordered with candidates |
| fallbacks | The chain chat() walks on transient errors before payment (timeout / network / 429 / 5xx) — candidates minus the primary, with ClawRouter's proxy-namespace free/* ids mapped to their nvidia/* gateway ids (SDK-computed) |
| savings | 0–1 fraction saved vs the baseline model (anthropic/claude-opus-4.7, priced like Opus 5) |
| costEstimate / baselineCost | Estimated cost of the pick vs that baseline, in USD |
| confidence | Sigmoid-calibrated classifier confidence, 0–1 |
| routerVersion | 'v3-portfolio' or 'v2-rules' |
| profile | Routing profile applied: 'auto', 'eco', 'premium', or 'agentic' |
| reasoning | Human-readable explanation of the decision |
| tierConfigs | The tier → primary/fallback map the decision was made against |
Search and routing. xAI search (x_search / web_search tools, or
search / searchParameters not set to mode: 'off') runs on xai/* models
only. When a routed call asks for search, the candidate chain is narrowed to
its xai/* models in the router's order. The router seldom ranks xAI, so when
the route has none, the call falls back to the catalog's search-capable xAI
models (those /v1/models tags search): cheapest first on eco / auto,
most capable first on premium. routing.reasoning says when that happened.
Only a catalog with no xai/* chat model at all makes route() /
smartChat() / smartChatCompletion() throw, before anything is sent or paid.
Pin an xai/* model yourself when you want a specific one.
TypeScript Types
Everything is typed. The routing types are derived from
@blockrun/router-core, pinned to
a reviewed immutable commit, and shipped inlined in this SDK's declaration
files and runtime bundle, so you install nothing extra to route or to
typecheck.
import {
LLMClient,
OpenAI,
APIError,
PaymentError,
type ChatMessage,
type ChatResponse,
type ChatOptions,
type ChatCompletionOptions,
type Model,
// Smart routing
type SmartChatOptions,
type SmartChatResponse,
type SmartChatCompletionOptions,
type SmartChatCompletionResponse,
type RoutingDecision,
type RoutingProfile,
type RoutingTier,
type RoutingTaskType,
type RoutingTierConfig,
// OpenAI-compatible layer
type OpenAIChatCompletionResponse,
type OpenAIChatCompletionChunk, // what the stream: true iterator yields
} from '@blockrun/llm';Going Deeper
- ClawRouter — the router itself: OpenClaw plugin, standalone proxy for Cursor / continue.dev / any OpenAI-compatible client, Telegram integration
- Routing profiles in depth — ECO / AUTO / PREMIUM details
- How the routing engine works — the classifier, dimension by dimension
- Router benchmark — sub-1ms routing across the catalog
- ClawRouter vs OpenRouter — head-to-head comparison
@blockrun/router-core— the deterministic routing engine both share
How Payment Works
In wallet mode no account or API key is needed. You hold USDC in your own wallet, and every request pays for itself with an on-chain micropayment. Two phases:
Phase 1 — Fund your wallet once
First, have a wallet. setupAgentClient() creates one for you (see
Wallets), or bring your own:
- Solana: create a wallet (Phantom, Backpack, Solflare, …) and set its bs58 secret key as
SOLANA_WALLET_KEY. - Base / Arc: create an EVM wallet (Coinbase Wallet, MetaMask, …) and set its private key as
BASE_CHAIN_WALLET_KEY.
# .env (never commit it)
SOLANA_WALLET_KEY=...your_bs58_secret_key
BASE_CHAIN_WALLET_KEY=0x...Then get USDC into it. You only do this when the balance runs low:
- (a) Buy with a card (Base USDC). Call
onramp()to mint a one-time Coinbase Onramp link, then open the returnedpay.coinbase.comURL: pay by card or bank in 60+ fiat currencies and the USDC lands in your wallet. The call itself is free. Onramp is Base-only (LLMClient), and the funding address must equal your signing wallet. - (b) Transfer existing USDC to your wallet address: Base USDC for
LLMClienton Base, USDC on Arc for Arc, Solana SPL USDC forSolanaLLMClient. - (c) Skip funding entirely. Use the free models: every call is $0, no balance required.
import { LLMClient } from '@blockrun/llm';
const client = new LLMClient();
const { url } = await client.onramp(client.getWalletAddress());
console.log(`Fund your wallet: ${url}`); // single-use, expires in ~5 min: mint it at click time
const balance = await client.getBalance(); // USDC on Base
console.log(`Balance: $${balance.toFixed(2)} USDC`);$5 of USDC covers thousands of paid requests. SolanaLLMClient has its own
getBalance() (USDC on Solana) and an async getWalletAddress().
Phase 2 — Every request pays for itself (automatic x402)
You just call client.smartChat(...) or client.chat(...); the payment is invisible:
- The SDK sends the request to BlockRun's gateway.
- The gateway answers 402 Payment Required with the price.
- The SDK signs the payment locally with your wallet key: an EIP-3009
USDC authorization (EIP-712 typed data) on Base or Arc for
LLMClient, an SPLTransferCheckedtransaction on Solana forSolanaLLMClient. - The request is retried automatically with the payment proof.
- The gateway's facilitator settles on-chain and the AI response comes back.
One call, no separate pay step. Free-tier models settle at $0 (no payment signed). You pay no gas on any chain: the facilitator submits the transfer and pays the network fee.
On Solana, step 3 pins the payment to a recent blockhash that is valid for
roughly 60 seconds. If one expires between signing and verification,
SolanaLLMClient re-signs against a fresh blockhash and retries — up to twice,
with a short backoff — rather than surfacing a payment error you would only
have to retry by hand.
The retry is deliberately narrow. It fires only when the gateway explicitly reports a verification-phase stale blockhash. Settlement failures, ambiguous rejections, insufficient funds and malformed responses all fail immediately. Verification runs strictly before settlement, so a retryable rejection means no transaction was broadcast and you cannot be charged twice.
Track spend and verify settlements
import { LLMClient, getCostSummary } from '@blockrun/llm';
const client = new LLMClient();
const spent = client.getSpending(); // this client, wallet mode only (throws with an API key)
console.log(`Spent $${spent.totalUsd.toFixed(4)} across ${spent.calls} calls`);
const summary = getCostSummary(); // across sessions, from ~/.blockrun/cost_log.jsonl
console.log(`Lifetime: $${summary.totalUsd.toFixed(2)} over ${summary.calls} calls`);
console.log('By model:', summary.byModel);Only LLMClient wallet settlements (Base / Arc chat) are appended to
~/.blockrun/cost_log.jsonl automatically. Other clients count their payments
in their own getSpending(). Account usage lives on the
Activity page, not in-process.
In wallet mode, every paid request is a real on-chain USDC transfer — look up your wallet address on Basescan (or a Solana or Arc explorer) to verify each settlement independently. The exception is Solana batch settlement: there the on-chain records are the channel deposit and BlockRun's batched redemptions, and each call is a signed voucher.
Your private key never leaves your machine: it is only used for local
signing. With per-request (exact) wallet payments, USDC moves from your
wallet for each request and BlockRun holds none of your funds. Two exceptions
to keep in mind: account credit is held by BlockRun on your account, and a
Solana batch channel escrows a deposit the operator can claim from, up to
maxDeposit.
Solana Support
Pay for AI calls with Solana USDC via sol.blockrun.ai:
import { SolanaLLMClient } from '@blockrun/llm';
// SOLANA_WALLET_KEY env var (bs58-encoded Solana secret key),
// or pass { privateKey: 'your-bs58-solana-key' } / { apiKey: 'brk_live_…' }
const client = new SolanaLLMClient();
// Same API as LLMClient: route first...
const routed = await client.smartChat('gm Solana: what is x402?');
console.log(routed.model, routed.response);
// ...or pin a model
const response = await client.chat('deepseek/deepseek-chat', 'gm Solana');
// Live Search with Grok
const trending = await client.chat('xai/grok-4.5', 'What is trending on X?', { search: true });
// xAI x_search on chosen accounts, structured output, real cost per call
const res = await client.chatCompletion('xai/grok-4.3', [
{ role: 'user', content: 'Which new models did these labs announce today? Return JSON.' },
], {
tools: [{ type: 'x_search', allowedXHandles: ['OpenAI', 'AnthropicAI', 'xai'], fromDate: '2026-10-04' }],
searchParameters: { maxTurns: 2 }, // bounds xAI's per-post search bill
responseFormat: { type: 'json_object' },
timeout: 180_000,
});
console.log(res.choices[0].message.annotations); // url_citation with positions
console.log(res.usage?.server_side_tool_usage); // { x_posts_fetched, ... }
console.log(res.costUsd, res.settlement); // what you paid, and the txcostUsd, settlement and the per-call timeout work the same on LLMClient
(Base / Arc). costUsd is what the call added to getSpending(): the signed
price under exact, the metered extra.chargedAmount under batch settlement,
0 for a free model; it is undefined in API-key mode (billed to the account).
When the gateway answers a paid request from a free fallback model it skips
settlement: costUsd is 0, settlement.skipped is "free-fallback", and
nothing is added to getSpending().
Setup:
- Export your Solana wallet key:
export SOLANA_WALLET_KEY="your-bs58-key" - Fund it with USDC on Solana mainnet
- That's it — payments are automatic via x402
Endpoint: https://sol.blockrun.ai/api · Payment: Solana USDC (SPL, mainnet)
Images, music and video on Solana:
import { SolanaLLMClient } from '@blockrun/llm';
const client = new SolanaLLMClient();
const image = await client.image('a lighthouse at dusk', { model: 'openai/gpt-image-2' });
const track = await client.music('chill lo-fi beats with piano');
// Extra video options are sent verbatim, so use the gateway's snake_case names.
const video = await client.video('a red apple slowly spinning', { duration_seconds: 5 });Slow jobs answer 202 { id, poll_url } and the client polls for you. Each poll
proves the job is yours with a wallet signature (x-poll-wallet,
x-poll-timestamp, x-poll-signature: ed25519 over
blockrun-poll:v1:<kind>:<job id>:<unix seconds>), not a payment, so a
multi-minute job does not sign a Solana transaction every few seconds. Image
and music are paid when submitted. A video is charged only once it is finished:
the client signs one payment for the completed job, and a failed job costs
nothing. If the gateway does not accept the poll signature for a job, the client
falls back to paying each poll the old way (logged to stderr). Base polling is
unchanged.
Batch settlement (optional metered billing)
By default every Solana call uses x402 exact: one SPL transfer per call,
priced at the request ceiling. Batch settlement replaces those per-call
transfers with a bounded payment channel: the client locks a small deposit
once, each request authorizes no more than its quoted ceiling, and the gateway
charges the metered cost after the response.
It is off unless you opt in. BLOCKRUN_SOL_OPERATOR is a public constant
exported by this SDK, not a secret or an environment variable; listing it in
batch.operators is the explicit decision that this wallet trusts BlockRun's
operator to sign vouchers against the channel, up to maxDeposit.
Batch mode needs three more optional peers on top of the Solana ones:
npm install @x402/core@~2.28.0 @x402/svm@~2.28.0 @solana/kitimport { SolanaLLMClient, BLOCKRUN_SOL_OPERATOR } from '@blockrun/llm';
const client = new SolanaLLMClient({
privateKey: process.env.SOLANA_WALLET_KEY,
batch: {
operators: [BLOCKRUN_SOL_OPERATOR], // explicit trust; there is no default
maxDeposit: '$5', // most escrow at stake at once (default "$1")
},
});
// The first call opens the channel and is charged its quoted price, as with exact.
await client.smartChat('gm');
// Later calls are metered: you pay for the tokens the call actually used.
const reply = await client.chat('anthropic/claude-sonnet-4.6', 'Summarize x402 in one line', {
maxTokens: 2048, // a ceiling, not a price
});
// Done for good? Close the channel to get the unused escrow back.
await client.closeBatchChannel();- Explicit trust.
operatorshas no default, and the SDK never learns a trusted key from a 402. - Bounded exposure.
maxDepositcaps the escrow deposited and not yet settled on-chain; it is the most the operator could claim without another authorization from you. - Never pays twice. Anything that goes wrong before a batch payment is sent falls back to
exact. Once one is sent, a call whose payment may have been charged throwsBatchPaymentUnresolvedErrorand is never paid again (no new payment, noexact, no fallback model). - Non-streaming chat only.
chat,chatCompletion,smartChatandsmartChatCompletionuse batch;stream()and media jobs payexact. - One channel per wallet. All clients for a wallet share one channel, saved to
~/.blockrun/solana-batch/<wallet>.json(mode0600) so a restart reuses it. - Observable. Every fallback, backoff, recovery and re-read writes one stderr line;
batch.onEventandclient.getBatchStats()expose the same events and counters.
The full runtime contract (the answer-by-answer table, payment-identifier replays, 429 backoff, deposit-in-doubt handling, crash safety, closing rules, RPC requirements, and every event reason) is in docs/solana-batch.md.
Arc Support
The same LLMClient pays on Circle's Arc via arc.blockrun.ai: point apiUrl at it and hold USDC on Arc in the same EVM wallet.
import { LLMClient } from '@blockrun/llm';
// BASE_CHAIN_WALLET_KEY: the same EVM key you use on Base
const client = new LLMClient({ apiUrl: 'https://arc.blockrun.ai/api' });
const routed = await client.smartChat('gm Arc');
const response = await client.chat('openai/gpt-5.4', 'gm Arc');The 402 from that host names eip155:5042, and the SDK signs the EIP-3009 authorization against Arc's USDC (0x3600…0000, EIP-712 domain USDC v2), never Base's. Circle's facilitator verifies and settles it on Arc; you pay no gas. Which networks the SDK can sign for is the exported EVM_NETWORKS table (Base, Arc, Base Sepolia); a 402 naming any other network, or a non-USDC asset, is refused before anything is signed.
Setup:
- Same wallet key as Base:
export BASE_CHAIN_WALLET_KEY="0x..." - Fund it with USDC on Arc (Arc's native token, shown as the ERC-20 at
0x3600…0000) apiUrl: 'https://arc.blockrun.ai/api'— payments are automatic via x402
Endpoint: https://arc.blockrun.ai/api · Payment: USDC on Arc (chain 5042), settled by Circle
Chat, Tools and Streaming
Simple chat
import { LLMClient } from '@blockrun/llm';
const client = new LLMClient(); // BLOCKRUN_API_KEY or BASE_CHAIN_WALLET_KEY
// Routed: the cheapest model that can handle it
const routed = await client.smartChat('Explain quantum computing in two paragraphs');
console.log(routed.response);
// Pinned, with a system prompt
const haiku = await client.chat('anthropic/claude-sonnet-5', 'Write a haiku', {
system: 'You are a creative poet.',
});Full chat completion
import { LLMClient, type ChatMessage } from '@blockrun/llm';
const client = new LLMClient();
const messages: ChatMessage[] = [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'How do I read a file in Node.js?' },
];
// Routed: the full OpenAI-style response plus the routing decision
const routed = await client.smartChatCompletion(messages);
console.log(routed.response.choices[0].message.content, routed.model);
// Or pinned
const result = await client.chatCompletion('anthropic/claude-sonnet-5', messages);
console.log(result.choices[0].message.content);Streaming
Every client streams with automatic payment. On LLMClient in Base / Arc
wallet mode, chatCompletionStream() also keeps a pre-auth cache: after the
first call to a model it signs the payment up front and skips the 402 round-trip
(about 200ms saved per request).
OpenAI-compatible (recommended)
import { OpenAI } from '@blockrun/llm';
const client = new OpenAI({ walletKey: process.env.BASE_CHAIN_WALLET_KEY }); // or { apiKey }
const stream = await client.chat.completions.create({
model: 'blockrun/auto', // routing aliases work here too
messages: [{ role: 'user', content: 'Write a short story about AI agents' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '');
}Native client
chatCompletionStream() returns the raw fetch Response. SSE events can span
network reads, so buffer partial lines:
import { LLMClient, type ChatMessage } from '@blockrun/llm';
const client = new LLMClient();
const messages: ChatMessage[] = [
{ role: 'user', content: 'Explain quantum computing in simple terms' },
];
const response = await client.chatCompletionStream('google/gemini-2.5-flash', messages);
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() ?? ''; // keep the incomplete last line for the next read
for (const line of lines) {
if (!line.startsWith('data: ') || line === 'data: [DONE]') continue;
const data = JSON.parse(line.slice(6));
process.stdout.write(data.choices?.[0]?.delta?.content || '');
}
}Solana
SolanaLLMClient.stream() has the same shape as BlockrunClient.stream(): it
yields each data: frame already parsed as JSON and stops at [DONE]. The
x402 handshake happens before the first frame — 402, sign an SPL
TransferChecked authorization locally, replay with PAYMENT-SIGNATURE —
including the fresh-blockhash re-sign on a verification-phase rejection. A free
model, or an account key, is answered 200 with no handshake at all and
settles nothing. Pass a concrete model id: stream() does not resolve the
blockrun/* aliases.
import { SolanaLLMClient } from '@blockrun/llm';
type ChatChunk = { choices?: { delta?: { content?: string } }[] };
const client = new SolanaLLMClient(); // SOLANA_WALLET_KEY, or { apiKey: 'brk_live_…' }
for await (const chunk of client.stream<ChatChunk>('/v1/chat/completions', {
model: 'deepseek/deepseek-chat',
messages: [{ role: 'user', content: 'Hi' }],
max_tokens: 64,
stream: true,
})) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? '');
}Set stream: true yourself — the method does not inject it, because the gateway
prices a streaming and a non-streaming request the same and rewriting a caller's
body silently is how you end up debugging a request you did not send.
Models and Service APIs
The live catalog is the source of truth for availability, context windows and pricing. Prices are not listed in this README: they change often. See blockrun.ai/models for live rates, or read them at runtime:
import { LLMClient } from '@blockrun/llm';
const client = new LLMClient();
const models = await client.listModels(); // chat models with pricing
for (const model of models) {
console.log(`${model.id}: $${model.inputPrice}/M input`);
}
const imageModels = await client.listImageModels();Free Tier
Input and output both $0 — no promo, no rate-limit gimmick. The free tier is
not NVIDIA-only, so pin these by full model id rather than by an nvidia/*
prefix, or let routingProfile: 'eco' rank them first.
| Model | Context | Notes |
|---|---|---|
| nvidia/nemotron-3.5-lightning | 1M | Thinking-mode reasoning at 1M context |
| nvidia/nemotron-3-ultra-550b | 1M | Largest free model — 550B, 1M context |
| nvidia/nemotron-3-nano-omni-30b-a3b-reasoning | 256K | Multimodal reasoning — text + images |
| nvidia/llama-3.2-11b-vision | 128K | Vision-language — accepts images |
| nvidia/muse-glimmer-30b | 128K | Chat, reasoning and coding |
| cohere/north-mini-code | 256K | Compact coding model, sub-second responses |
| poolside/laguna-xs-2.1 | 128K | Coding model |
A free model can be briefly marked unavailable in the live catalog while the gateway's health check holds it back; it stays on the free tier.
OpenAI
GPT-6: Astra is the flagship for long-horizon agentic work and computer use,
Sol the cost-efficient tier for complex coding, Luna the fast low-cost tier.
GPT-5.6: three tiers on one base (Sol deepest reasoning, Terra balanced, Luna
cheap and fast), each with a -pro sibling that thinks longer at the same
token price.
| Model | Context | Notes |
|---|---|---|
| openai/gpt-6.1-sol | 1.05M | |
| openai/gpt-6-astra | 1.05M | GPT-6 flagship |
| openai/gpt-6-sol | 1.05M | |
| openai/gpt-6-luna | 1.05M | |
| openai/gpt-5.6-sol | 1.05M | |
| openai/gpt-5.6-sol-pro | 1.05M | |
| openai/gpt-5.6-terra | 1.05M | |
| openai/gpt-5.6-terra-pro | 1.05M | |
| openai/gpt-5.6-luna | 1.05M | |
| openai/gpt-5.6-luna-pro | 1.05M | |
| openai/gpt-5.5 | 1.05M | |
| openai/gpt-5.5-pro | 1.05M | |
| openai/chat-latest | 128K | ChatGPT Instant — the model behind chatgpt.com |
| openai/gpt-5.4 | 1.05M | |
| openai/gpt-5.4-pro | 1.05M | |
| openai/gpt-5.4-mini | 400K | |
| openai/gpt-5.4-nano | 1.05M | |
| openai/gpt-5.3-codex | 400K | Coding/agentic SKU |
| openai/gpt-5.2 | 400K | |
| openai/gpt-5.2-pro | 400K | |
| openai/gpt-5.1 | 400K | Configurable reasoning effort |
| openai/gpt-5-mini | 200K | |
| openai/gpt-4.1 | 128K | |
| openai/gpt-4.1-mini | 128K | |
| openai/gpt-4.1-nano | 128K | |
| openai/gpt-4o | 128K | |
| openai/gpt-4o-mini | 128K | |
| openai/o1 | 200K | Reasoning |
| openai/o3 | 200K | Reasoning |
| openai/o3-mini | 128K | Reasoning |
| openai/o4-mini | 128K | Reasoning |
Anthropic Claude
| Model | Context | Notes |
|---|---|---|
| anthropic/claude-fable-5.1 | 1M | Most capable — successor to Fable 5 at the same tier and price |
| anthropic/claude-fable-5 | 1M | Mythos-class flagship above Opus — always-on thinking, 128K output |
| anthropic/claude-opus-5.5 | 1M | Newest Opus — Opus-class reasoning at a lower price than Opus 5 |
| anthropic/claude-opus-5 | 1M | Flagship — the baseline the routing savings claim is measured against |
| anthropic/claude-opus-4.8 | 1M | Agentic coding + adaptive thinking, 128K output |
| anthropic/claude-opus-4.7 | 1M | Router-core's per-request savings baseline (priced like Opus 5) |
| anthropic/claude-opus-4.5 | 200K | |
| anthropic/claude-sonnet-5.5 | 1M | Newest Sonnet — everyday coding and agent work, 128K output |
| anthropic/claude-sonnet-5 | 1M | Best cost/quality balance for long-context agent turns |
| anthropic/claude-sonnet-4.6 | 1M | |
| anthropic/claude-sonnet-4.5 | 200K | |
| anthropic/claude-haiku-4.5 | 200K | |
Google Gemini
| Model | Context |
|---|---|
| google/gemini-3.1-pro | 1M |
| google/gemini-3.8-flash | 1M |
| google/gemini-3.6-flash | 1M |
| google/gemini-3.5-flash | 1M |
| google/gemini-3-flash-preview | 1M |
| google/gemini-3.5-flash-lite | 1M |
| google/gemini-3.1-flash-lite | 1M |
| google/gemini-2.5-pro | 1M |
| google/gemini-2.5-flash | 1M |
| google/gemini-2.5-flash-lite | 1M |
DeepSeek
DeepSeek upstream serves the legacy deepseek-chat / deepseek-reasoner
aliases as V4 Flash non-thinking / thinking modes. V4 Pro is the flagship
paid SKU; the vision SKU is an experimental preview.
| Model | Context | Notes |
|---|---|---|
| deepseek/deepseek-v4-pro | 1M | V4 flagship — strongest open-weight reasoner |
| deepseek/deepseek-v4-flash-vision-exp | 1M | Experimental vision preview |
| deepseek/deepseek-chat | 1M | V4 Flash non-thinking |
| deepseek/deepseek-reasoner | 1M | V4 Flash thinking (same upstream, thinking on by default) |
xAI Grok
The older Grok chat SKUs (grok-3/3-mini, the grok-4 fast families, grok-code-fast-1, grok-2-vision) have left the catalog. Retired ids stay callable — the gateway redirects them to a healthy model.
| Model | Context | Notes |
|---|---|---|
| xai/grok-4.7 | 500K | Flagship — reasoning + vision, selectable effort (low → xhigh), native Live Search (search: true) |
| xai/grok-4.6 | 500K | Reasoning with selectable effort, native Live Search |
| xai/grok-4.5 | 500K | Reasoning + vision, native Live Search |
| xai/grok-4.3 | 1M | Reasoning + vision, tuned for agentic workflows |
| xai/grok-build-0.1 | 256K | Fast agentic coding model |
Moonshot, MiniMax, Z.ai, Qwen
| Model | Context | Notes |
|---|---|---|
| moonshot/kimi-k3 | 1M | Replaces the retired kimi-k2.5 / k2.6 SKUs |
| minimax/minimax-m3 | 1M | |
| minimax/minimax-m2.7 | 200K | |
| zai/glm-5.3 | 1M | |
| zai/glm-5.3-flash | 1M | Low-cost vision-capable GLM |
| zai/glm-5.2 | 1M | |
| zai/glm-5.1 | 200K | |
| zai/glm-5 | 200K | |
| zai/glm-5-turbo | 200K | |
| qwen/qwen3.8-max | 1M | |
| qwen/qwen3.7-max | 1M | |
| qwen/qwen3.7-plus | 1M | |
| qwen/qwen3.8-flash | 1M | |
| qwen/qwen3.7-flash | 1M | Cheapest paid chat model in the catalog |
Mistral, Tencent, Poolside
| Model | Context | Notes |
|---|---|---|
| mistral/mistral-large-4 | 512K | Reasoning + vision, 256K output |
| tencent/hy4-preview | 1M | Preview |
| poolside/laguna-s-2.1 | 1M | Paid coding model (sibling of the free XS) |
Xiaomi
| Model | Context |
|---|---|
| xiaomi/mimo-v2.5 | 1M |
| xiaomi/mimo-v2.5-pro | 1M |
Testnet Models (Base Sepolia)
| Model |
|-------|
| openai/gpt-oss-20b |
| openai/gpt-oss-120b |
Testnet models use flat pricing (no token counting) for simplicity.
BlockrunClient — the universal primitive
BlockrunClient speaks to any BlockRun endpoint, so new endpoints work
without an SDK release. It takes an account apiKey or an EVM wallet key
(BLOCKRUN_WALLET_KEY or BASE_CHAIN_WALLET_KEY); its wallet mode pays on
Base / Arc only and refuses a solana: 402, so Solana wallets use
SolanaLLMClient.
import { BlockrunClient } from '@blockrun/llm';
const br = new BlockrunClient();
// Sync GET: crypto price
const btc = await br.get('/v1/crypto/price/BTC-USD');
// Sync POST: live web search
const res = await br.post('/v1/search', { query: 'x402 adoption', sources: ['web'] });
// Submit + poll: long-running video generation (settled only on completion)
const video = await br.poll('/v1/videos/generations', {
model: 'xai/grok-imagine-video',
prompt: 'a red apple spinning',
});
// Streaming SSE: chat completions
type ChatChunk = { choices?: { delta?: { content?: string } }[] };
for await (const chunk of br.stream<ChatChunk>('/v1/chat/completions', {
model: 'anthropic/claude-sonnet-5',
messages: [{ role: 'user', content: 'Hi' }],
stream: true,
})) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? '');
}Four call shapes cover every endpoint type:
get<T>(path, params?)— synchronous GET (price, list, history)post<T>(path, body?)— synchronous POST (search, RPC)poll<T>(path, body?, { budgetMs, intervalMs })— submit + poll (image, video, music)stream<T>(path, body?)— async iterator over SSE chunks (chat)
The per-API clients below remain fully supported; BlockrunClient is the
generic escape hatch for endpoints without a typed wrapper.
Image Generation and Editing
| Model | Notes |
|---|---|
| openai/gpt-image-1 | Native GPT-4o image generation; also edits |
| openai/gpt-image-2 | Reasoning-driven — multilingual text rendering, character consistency; also edits (the default) |
| openai/gpt-image-2.5-flare | GPT Image 2.5 — fast, everyday quality |
| openai/gpt-image-2.5-sunburst | GPT Image 2.5 — most capable; also edits (incl. mask) |
| google/nano-banana | Gemini 2.5 Flash image generation — fast and efficient; also edits |
| google/nano-banana-2 | Gemini 3.1 Flash — pro-level quality at Flash speed; also edits |
| google/nano-banana-pro | Gemini 3 Pro — highest quality, up to 4K; also edits |
| xai/grok-imagine-image | Fast, 300 RPM |
| xai/grok-imagine-image-2.0 | Between Grok Imagine and Pro |
| xai/grok-imagine-image-pro | Quality tier, 30 RPM |
| bytedance/seedream-5-pro | Flagship generation + editing, up to 4K-class, reference images |
| zai/cogview-4 | Up to 1440x1440 |
ImageClient.edit() calls /v1/images/image2image with the edit-capable
models marked above. Source images must be base64 data:image/... URIs (not
URLs). Pass one to edit it, or an array of 2–4 to fuse them (e.g. a subject
and a brand logo): openai/* takes up to 4 sources, google/* up to 3. A
mask is OpenAI-only and cannot be combined with multiple sources.
LLMClient.imageEdit() and SolanaLLMClient.imageEdit() take the same
arguments.
import { ImageClient } from '@blockrun/llm';
const client = new ImageClient();
const generated = await client.generate('a lighthouse at dusk, oil painting', {
model: 'google/nano-banana',
});
console.log(generated.data[0].url);
const photoDataUri = 'data:image/png;base64,...';
const subjectDataUri = 'data:image/png;base64,...';
const logoDataUri = 'data:image/png;base64,...';
// Edit one image
const edited = await client.edit('Make the sky purple and add northern lights', photoDataUri, {
model: 'openai/gpt-image-2',
});
// Fuse several images
const fused = await client.edit('Place the logo on the t-shirt', [subjectDataUri, logoDataUri], {
model: 'google/nano-banana',
});
console.log(fused.data[0].url);Video Generation
| Model | Default | Max | Notes |
|---|---|---|---|
| xai/grok-imagine-video | 8s | 15s | 480p default, 720p available; text or image to video |
| xai/grok-imagine-video-1.5 | 8s | 15s | Native synced audio; 480p default, 720p / 1080p available |
| bytedance/seedance-1.5-pro | 5s | 12s | Budget, up to 1080p, synced audio. No RealFace |
| bytedance/seedance-2.0-fast | 5s | 15s | 480p/720p, ~60-80s to generate. RealFace supported |
| bytedance/seedance-2.0-mini | 5s | 15s | 480p/720p, lowest-cost Seedance 2.0. RealFace supported |
| bytedance/seedance-2.0 | 5s | 15s | Premium, up to 4K, synced audio. RealFace supported |
| bytedance/seedance-2.5 | 5s | 30s | Long-form — up to 30s, 480p/720p, multilingual, up to 30 reference images |
| azure/sora-2 | 4s | 12s | Sora 2 via Azure AI Foundry — 720p with synced audio; 4, 8 or 12s |
import { VideoClient } from '@blockrun/llm';
const client = new VideoClient();
const result = await client.generate('a red apple slowly spinning on a wooden table');
console.log(result.data[0].url); // permanent MP4 URL
console.log(result.data[0].duration_seconds); // 8
// Image-to-video
const fromImage = await client.generate('the subject turns and smiles', {
imageUrl: 'https://example.com/portrait.jpg',
});
// Seedance options (each is model-gated: an unsupported one is a 400 before payment)
const seedance = await client.generate('aerial drone shot over a snowy mountain', {
model: 'bytedance/seedance-2.0',
aspectRatio: '21:9',
resolution: '1080p',
generateAudio: true, // omit to use the model's default
seed: 42,
returnLastFrame: true, // useful for clip chaining
});
console.log(seedance.data[0].last_frame_url); // present when the upstream returns itSeedance also supports first-and-last-frame interpolation (lastFrameUrl),
output controls (bitrateMode, outputFormat, cameraFixed), and
reference images, video and audio (referenceImageUrls, referenceVideos,
referenceAudios). Reference media is served by api.blockrun.ai, so it needs
an account API key; the wallet gateways refuse it with a 400 before any
payment. Each option's model support and limits are documented on
VideoGenerateOptions in the TSDoc.
Music Generation
MusicClient wraps POST /v1/audio/generations with minimax/music-2.5+ (the
default). A track takes 1–3 minutes; the returned URL is valid for about 24 hours,
so download it.
import { MusicClient } from '@blockrun/llm';
const client = new MusicClient();
const instrumental = await client.generate('chill lo-fi beats with piano');
console.log(instrumental.data[0].url);
// With vocals: pass lyrics and turn instrumental off
const song = await client.generate('upbeat pop song', {
instrumental: false,
lyrics: 'Hello world, this is my song...',
});Text-to-Speech & Sound Effects
SpeechClient wraps BlockRun Voice: POST /v1/audio/speech (OpenAI-compatible
TTS), POST /v1/audio/sound-effects, and the free GET /v1/audio/voices. The
ElevenLabs TTS models are priced by character count ((chars / 1000) × model
rate, with a per-request minimum); Seed Audio is priced by output duration.
Synthesis is synchronous (<1s for Flash).
| Model | Max Input | Notes |
|---|---|---|
| elevenlabs/flash-v2.5 | 40k chars | ~75ms latency, 32 languages (default) |
| elevenlabs/turbo-v2.5 | 40k chars | ~250ms latency, balanced quality |
| elevenlabs/multilingual-v2 | 10k chars | Long-form narration, audiobooks — 29 languages |
| elevenlabs/v3 | 5k chars | Max expressiveness, 70+ languages |
| bytedance/seed-audio-1.0 | 3k chars | Prompt-directed — describe voice, emotion and staging in words; billed by output duration (up to 120s) |
| elevenlabs/sound-effects | 1k chars | Sound effects up to 22s |
import { SpeechClient } from '@blockrun/llm';
const client = new SpeechClient();
// Text-to-speech (voice aliases: sarah, george, laura, charlie,
// river, roger, callum, harry — or any raw ElevenLabs voice_id)
const result = await client.generate('Welcome to BlockRun.', { voice: 'george' });
console.log(result.data[0].url); // audio URL (mp3 by default)
// Other formats / speed
const wav = await client.generate('Breaking news from the world of micropayments.', {
model: 'elevenlabs/v3',
responseFormat: 'wav',
speed: 1.1,
});
// Sound effects (flat per generation)
const fx = await client.soundEffect('rain on a tin roof, distant thunder');
// List voices (free, rate-limited)
const voices = await client.listVoices();Virtual Portraits
PortraitClient wraps POST /v1/portrait/enroll (paid, flat promo rate,
no KYC). Enroll a face image by URL and get back a Token360 asset id (ta_xxxxxx).
Pass that id as realFaceAssetId on a Seedance 2.0 video generation to keep the
same AI character across clips. Payment settles only after Token360 confirms the
enrollment, so a failed enrollment never charges your wallet. The returned
image_url is a gateway-mirrored copy of your source image (see mirrored /
source_image_url). (Real-person likeness is not supported on BlockRun —
enrolled portraits are AI characters.)
import { PortraitClient, VideoClient } from '@blockrun/llm';
const portraits = new PortraitClient();
const { asset_id } = await portraits.enroll({
name: 'Spokesperson',
imageUrl: 'https://example.com/face.jpg', // public https JPG/PNG/WEBP, ≤10 MB
});
// Reuse the same character across Seedance 2.0 clips
const video = new VideoClient();
const clip = await video.generate('she waves and smiles', {
model: 'bytedance/seedance-2.0-fast',
realFaceAssetId: asset_id,
});
console.log(clip.data[0].url);Voice Calls and Phone Numbers
VoiceClient wraps POST /v1/voice/call (paid, flat per call) and
GET /v1/voice/call/{callId} (free polling) — AI-powered outbound phone
calls powered by Bland.ai. The agent dials the recipient and runs a real-time
conversation based on your task instructions. US + Canada destinations.
import { VoiceClient } from '@blockrun/llm';
const client = new VoiceClient();
// Initiate (paid)
const result = await client.call({
to: '+14155552671',
task: 'You are a friendly assistant calling to confirm a 3pm dentist appointment.',
voice: 'maya', // 'nat' | 'josh' | 'maya' | 'june' | 'paige' | 'derek' | 'florian'
max_duration: 5, // minutes (1–30)
});
console.log(result.call_id);
// Poll for transcript + recording (free)
const status = await client.getStatus(result.call_id);
console.log(status.status, status.recording_url);PhoneClient covers phone lookups and numbers: lookup() (carrier and line
type) and lookupFraud() (adds SIM-swap and call-forwarding signals), plus
buyNumber({ country, areaCode }) for a 30-day US/CA number, renewNumber(),
listNumbers() and releaseNumber(). A number you own can be the caller ID
of a voice call: pass it as from.
import { PhoneClient } from '@blockrun/llm';
const phone = new PhoneClient();
const info = await phone.lookup('+14155552671');
const { phone_number } = await phone.buyNumber({ country: 'US', areaCode: '415' });Standalone Search
SearchClient wraps POST /v1/search — Grok Live Search over web and
news, at a flat price per call. For X/Twitter search, use chat on an xai/*
model with an x_search tool or searchParameters: { sources: [{ type: 'x' }] }
(see Solana Support for an example).
import { SearchClient } from '@blockrun/llm';
const client = new SearchClient();
const result = await client.search('Latest news on x402 adoption', {
sources: ['web', 'news'],
maxResults: 10,
});
console.log(result.summary);
for (const c of result.citations ?? []) console.log(c.url);The same endpoint is available as search() on LLMClient and
SolanaLLMClient.
Market Data (Pyth)
PriceClient wraps the Pyth-backed market-data endpoints. Crypto, FX and
commodity are free (price + history + list); 12 global stock markets and the
usstock legacy alias are billed per call for price + history (list is always
free). Pass requireWallet: false to construct a free-only client.
import { PriceClient } from '@blockrun/llm';
const p = new PriceClient({ requireWallet: false });
const btc = await p.price('crypto', 'BTC-USD');
const eur = await p.price('fx', 'EUR-USD');
const symbols = await p.listSymbols('crypto', { query: 'sol', limit: 20 });
// Paid: requires an API key or a wallet
const p2 = new PriceClient();
const aapl = await p2.price('stocks', 'AAPL', { market: 'us' });
const bars = await p2.history('stocks', 'AAPL', {
market: 'us',
resolution: 'D',
from: 1700000000,
to: 1710000000,
});Supported StockMarket values: us, hk, jp, kr, gb, de, fr, nl, ie, lu, cn, ca.
Prediction Markets (Powered by Predexon)
Prediction-market data from Polymarket, Kalshi, Limitless, Opinion,
Predict.Fun, Binance Futures, UMA oracle resolutions and sports markets via
Predexon. Every endpoint costs the same flat price per
call ($0.0085 at the time of writing). Use an account API key or x402 wallet
payments; no separate Predexon key is needed. pm() works on both LLMClient
and SolanaLLMClient; query parameters are strings.
import { LLMClient } from '@blockrun/llm';
const client = new LLMClient();
// Polymarket
const markets = await client.pm('polymarket/markets', { status: 'active', limit: '10' });
const events = await client.pm('polymarket/events');
const candles = await client.pm('polymarket/candlesticks/0x1234abcd...');
const profile = await client.pm('p