OpenRouter is TanStack AI's first official AI partner and the recommended starting point for most projects. It provides access to 300+ models from OpenAI, Anthropic, Google, Meta, Mistral, and many more — all through a single API key and unified interface.
npm install @tanstack/ai-openrouternpm install @tanstack/ai-openrouterimport { chat } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages: [{ role: "user", content: "Hello!" }],
});import { chat } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages: [{ role: "user", content: "Hello!" }],
});import { createOpenRouterText } from "@tanstack/ai-openrouter";
const adapter = createOpenRouterText(
"openai/gpt-5",
process.env.OPENROUTER_API_KEY!,
{
serverURL: "https://openrouter.ai/api/v1", // Optional
httpReferer: "https://your-app.com", // Optional, for rankings
appTitle: "Your App Name", // Optional, for rankings
},
);import { createOpenRouterText } from "@tanstack/ai-openrouter";
const adapter = createOpenRouterText(
"openai/gpt-5",
process.env.OPENROUTER_API_KEY!,
{
serverURL: "https://openrouter.ai/api/v1", // Optional
httpReferer: "https://your-app.com", // Optional, for rankings
appTitle: "Your App Name", // Optional, for rankings
},
);OpenRouter provides access to 300+ models from various providers. Models use the format provider/model-name:
model: "openai/gpt-5.1"
model: "anthropic/claude-sonnet-4.5"
model: "google/gemini-3-pro-preview"
model: "meta-llama/llama-4-maverick"
model: "deepseek/deepseek-v3.2"model: "openai/gpt-5.1"
model: "anthropic/claude-sonnet-4.5"
model: "google/gemini-3-pro-preview"
model: "meta-llama/llama-4-maverick"
model: "deepseek/deepseek-v3.2"See the full list at openrouter.ai/models.
import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
export async function POST(request: Request) {
const { messages } = await request.json();
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages,
});
return toServerSentEventsResponse(stream);
}import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
export async function POST(request: Request) {
const { messages } = await request.json();
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages,
});
return toServerSentEventsResponse(stream);
}import { chat, toolDefinition } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
import { z } from "zod";
const getWeatherDef = toolDefinition({
name: "get_weather",
description: "Get the current weather",
inputSchema: z.object({
location: z.string(),
}),
});
const getWeather = getWeatherDef.server(async ({ location }) => {
return { temperature: 72, conditions: "sunny" };
});
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages,
tools: [getWeather],
});import { chat, toolDefinition } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
import { z } from "zod";
const getWeatherDef = toolDefinition({
name: "get_weather",
description: "Get the current weather",
inputSchema: z.object({
location: z.string(),
}),
});
const getWeather = getWeatherDef.server(async ({ location }) => {
return { temperature: 72, conditions: "sunny" };
});
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages,
tools: [getWeather],
});Set your API key in environment variables:
OPENROUTER_API_KEY=sk-or-...OPENROUTER_API_KEY=sk-or-...OpenRouter can automatically route requests to the best available provider:
const stream = chat({
adapter: openRouterText("openrouter/auto"),
messages,
modelOptions: {
models: [
"openai/gpt-4o",
"anthropic/claude-3.5-sonnet",
"google/gemini-pro",
],
},
});const stream = chat({
adapter: openRouterText("openrouter/auto"),
messages,
modelOptions: {
models: [
"openai/gpt-4o",
"anthropic/claude-3.5-sonnet",
"google/gemini-pro",
],
},
});OpenRouter exposes two OpenAI-compatible wire formats, and the adapter package ships one of each:
| Adapter | Endpoint | Status | When to use |
|---|---|---|---|
| openRouterText | /v1/chat/completions | Stable | Default for almost everything. Broadest model + tool support. |
| openRouterResponsesText | /v1/responses | Beta | OpenAI Responses-shaped request/response; richer multi-turn state on OpenAI-style models. |
Both adapters route to any underlying model OpenRouter supports (anthropic/..., google/..., meta-llama/..., etc.) — the wire format describes how your client talks to OpenRouter, not which provider answers. /v1/responses is OpenAI's newer API surface; OpenRouter implements it so clients that prefer that wire format can use it across the same 300+ model catalogue.
import { chat } from "@tanstack/ai";
import { openRouterResponsesText } from "@tanstack/ai-openrouter";
const stream = chat({
adapter: openRouterResponsesText("anthropic/claude-sonnet-4.5"),
messages: [{ role: "user", content: "Hello!" }],
});import { chat } from "@tanstack/ai";
import { openRouterResponsesText } from "@tanstack/ai-openrouter";
const stream = chat({
adapter: openRouterResponsesText("anthropic/claude-sonnet-4.5"),
messages: [{ role: "user", content: "Hello!" }],
});Caveats while the Responses adapter is in beta:
Migrated from createWebSearchTool? This factory was renamed to webSearchTool and moved to the /tools subpath in this release. See Migration Guide §6 for the exact before/after.
OpenRouter's gateway exposes web search via a plugin that works across any proxied chat model. Import it from @tanstack/ai-openrouter/tools.
For the full concept, a comparison matrix, and type-gating details, see Provider Tools.
Adds web search capability to any OpenRouter-proxied chat model. The factory accepts OpenRouter's WebSearchConfig directly — pick the engine (auto, native, exa, firecrawl, or parallel), cap results with maxResults / maxTotalResults, restrict which sites can appear in results with allowedDomains / excludedDomains, and optionally pass searchContextSize or userLocation for finer control.
import { chat } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
import { webSearchTool } from "@tanstack/ai-openrouter/tools";
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages: [{ role: "user", content: "What's new in AI this week?" }],
tools: [
webSearchTool({
engine: "exa",
maxResults: 5,
allowedDomains: ["arxiv.org", "openai.com"],
}),
],
});import { chat } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
import { webSearchTool } from "@tanstack/ai-openrouter/tools";
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages: [{ role: "user", content: "What's new in AI this week?" }],
tools: [
webSearchTool({
engine: "exa",
maxResults: 5,
allowedDomains: ["arxiv.org", "openai.com"],
}),
],
});Supported models: all OpenRouter chat models. See Provider Tools.
Lets any OpenRouter-proxied chat model fetch the full contents of a URL the model chooses, instead of running a search. The factory accepts OpenRouter's WebFetchServerToolConfig directly — pick the fetch engine (auto — the default, native, openrouter, exa, or firecrawl), cap how much page content the model receives with maxContentTokens, cap how many fetches the model can make per request with maxUses, and restrict which URLs the model can fetch with allowedDomains / blockedDomains.
The native engine routes to the underlying provider's own fetch (for example, Anthropic's web_fetch on Claude models). Native fetch capabilities vary, so allowedDomains and blockedDomains may be ignored. Use openrouter, exa, or firecrawl if you need consistent behaviour across models.
import { chat } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
import { webFetchTool } from "@tanstack/ai-openrouter/tools";
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages: [
{ role: "user", content: "Summarize https://example.com/article" },
],
tools: [
webFetchTool({
engine: "openrouter",
maxContentTokens: 4000,
allowedDomains: ["example.com"],
}),
],
});import { chat } from "@tanstack/ai";
import { openRouterText } from "@tanstack/ai-openrouter";
import { webFetchTool } from "@tanstack/ai-openrouter/tools";
const stream = chat({
adapter: openRouterText("openai/gpt-5"),
messages: [
{ role: "user", content: "Summarize https://example.com/article" },
],
tools: [
webFetchTool({
engine: "openrouter",
maxContentTokens: 4000,
allowedDomains: ["example.com"],
}),
],
});Supported models: all OpenRouter chat models. See Provider Tools.