npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.

npm npm downloads CI License: MIT Node TypeScript

API Key Solana Base Network x402 Telegram

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 proof

84% 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 / messages layers, 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 included
npm install @blockrun/llm @solana/web3.js @solana/spl-token

Why 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

  1. Sign up or sign in.
  2. Create a key on API Keys and add credit on Credits.
  3. 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 model

Base 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, a 503 PAYMENT_VERIFICATION_UNAVAILABLE in 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, &lt;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

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 returned pay.coinbase.com URL: 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 LLMClient on Base, USDC on Arc for Arc, Solana SPL USDC for SolanaLLMClient.
  • (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:

  1. The SDK sends the request to BlockRun's gateway.
  2. The gateway answers 402 Payment Required with the price.
  3. 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 SPL TransferChecked transaction on Solana for SolanaLLMClient.
  4. The request is retried automatically with the payment proof.
  5. 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 tx

costUsd, 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:

  1. Export your Solana wallet key: export SOLANA_WALLET_KEY="your-bs58-key"
  2. Fund it with USDC on Solana mainnet
  3. 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/kit
import { 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. operators has no default, and the SDK never learns a trusted key from a 402.
  • Bounded exposure. maxDeposit caps 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 throws BatchPaymentUnresolvedError and is never paid again (no new payment, no exact, no fallback model).
  • Non-streaming chat only. chat, chatCompletion, smartChat and smartChatCompletion use batch; stream() and media jobs pay exact.
  • One channel per wallet. All clients for a wallet share one channel, saved to ~/.blockrun/solana-batch/<wallet>.json (mode 0600) so a restart reuses it.
  • Observable. Every fallback, backoff, recovery and re-read writes one stderr line; batch.onEvent and client.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:

  1. Same wallet key as Base: export BASE_CHAIN_WALLET_KEY="0x..."
  2. Fund it with USDC on Arc (Arc's native token, shown as the ERC-20 at 0x3600…0000)
  3. 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 it

Seedance 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