OpenRouter

May 22, 2026 · View on GitHub

OpenRouter provides a unified API to access models from OpenAI, Anthropic, Google, Meta, and many other providers through a single endpoint.

Configuration

{
	"name": "OpenRouter",
	"baseUrl": "https://openrouter.ai/api/v1",
	"apiKey": "your-openrouter-api-key",
	"models": ["provider/model-name"]
}

Setup

  1. Create an account at openrouter.ai
  2. Generate an API key from the keys page
  3. Browse available models at openrouter.ai/models

Model names follow the format provider/model-name.

OpenRouter request options

Nanocoder forwards OpenRouter-specific request body fields through an openrouter block on the provider config. These are always-on for the OpenRouter provider — they are not gated by tune, so routing rules apply on every request regardless of session state.

The provider is detected by name — any provider entry called openrouter (case-insensitive) picks these options up. If you put an openrouter block on a provider with a different name, nanocoder logs a warning at startup so the misconfiguration is visible immediately.

How this compares to tune. Tune covers runtime model behaviour (temperature, tool profile, compaction, reasoning effort) and can be toggled or persisted per-session via the /tune modal. The openrouter block covers transport and routing concerns (which upstream provider serves the request, at what tier, with which fallbacks) — these are static, file-only, and never disabled. The one bridge between them is tune.modelParameters.reasoningEffort, which populates openrouter.reasoning.effort when the latter is unset. Explicit values on the provider config always win.

{
	"providers": [
		{
			"name": "OpenRouter",
			"baseUrl": "https://openrouter.ai/api/v1",
			"apiKey": "${OPENROUTER_API_KEY}",
			"models": ["anthropic/claude-4.5-sonnet"],
			"openrouter": {
				"provider": {"sort": "price", "allow_fallbacks": true},
				"reasoning": {"effort": "high"},
				"service_tier": "flex"
			}
		}
	]
}
FieldTypeDescription
providerobjectProvider routing rules (see below)
reasoningobjectReasoning token controls (effort, max_tokens, exclude, enabled)
pluginsobject[]OpenRouter plugin pipeline (replaces the legacy transforms field)
modelsstring[]Fallback model list, tried in order if the primary model fails
service_tier"flex" | "priority"Pricing/latency tier — flex is cheaper/slower, priority is faster/pricier
route"fallback"Top-level routing toggle
userstringStable end-user identifier passed to upstream providers
extraBodyobjectEscape hatch for arbitrary OpenRouter body fields not yet typed

The per-section examples below show only the openrouter slice. In your agents.config.json, that slice sits inside the OpenRouter provider entry alongside name, baseUrl, apiKey, and models — see the full example above.

Provider routing

{
	"openrouter": {
		"provider": {
			"order": ["Anthropic", "OpenAI"],
			"allow_fallbacks": false,
			"require_parameters": true,
			"data_collection": "deny",
			"sort": "throughput",
			"only": ["Anthropic"],
			"ignore": ["DeepInfra"],
			"quantizations": ["bf16", "fp16"],
			"zdr": true,
			"enforce_distillable_text": true,
			"max_price": {"prompt": 0.5, "completion": 1.5},
			"preferred_min_throughput": {"p90": 30},
			"preferred_max_latency": 2000
		}
	}
}
FieldTypeNotes
orderstring[]Preferred provider order
allow_fallbacksbooleanFall back to other providers if preferred ones fail
require_parametersbooleanOnly use providers that support every parameter you send
data_collection"allow" | "deny"Restrict to providers honouring the chosen policy
onlystring[]Whitelist providers
ignorestring[]Blacklist providers
quantizationsstring[]Only providers serving the listed quantisations
sortstring | objectEither "price", "throughput", "latency", or {"by": …, "partition": "model"|"none"} for cross-model fallback sorting
zdrbooleanEnforce Zero Data Retention
enforce_distillable_textbooleanSkip providers that apply lossy text transforms
max_priceobjectCap pricing per prompt / completion / request / image
preferred_min_throughputnumber | {p50,p75,p90,p99}Throughput floor (tokens/s)
preferred_max_latencynumber | {p50,p75,p90,p99}Latency ceiling (ms)

Full reference: openrouter.ai/docs/features/provider-routing.

Reasoning tokens

{
	"openrouter": {
		"reasoning": {
			"effort": "xhigh",
			"max_tokens": 8000,
			"exclude": false,
			"enabled": true
		}
	}
}

effort accepts "xhigh", "high", "medium", "low", "minimal", or "none".

The cross-provider tune.modelParameters.reasoningEffort field (minimal | low | medium | high) is also honoured for OpenRouter — it maps to reasoning.effort automatically. An explicit openrouter.reasoning.effort on the provider config wins over the tune shortcut.

Full reference: openrouter.ai/docs/use-cases/reasoning-tokens.

Plugins

The OpenRouter plugin pipeline replaces the legacy top-level transforms field. Pass an array of plugin objects, each with an id:

{
	"openrouter": {
		"plugins": [
			{"id": "context-compression", "engine": "middle-out"},
			{"id": "web"}
		]
	}
}

Fallback models

models declares an ordered fallback list. OpenRouter tries each in turn if the primary model returns an error or is unavailable:

{
	"openrouter": {
		"models": ["anthropic/claude-3.5-sonnet", "openai/gpt-4o"]
	}
}

See openrouter.ai/docs/features/model-routing.

Service tier

{
	"openrouter": {
		"service_tier": "flex"
	}
}

flex routes through cheaper, higher-latency capacity. priority is more expensive with lower latency. Reference: openrouter.ai/docs/guides/features/service-tiers.

Generic body pass-through

For OpenRouter body fields that don't have a dedicated typed entry yet, use extraBody. It's shallow-merged into the request body before the typed fields above, so typed fields win on key conflicts:

{
	"openrouter": {
		"extraBody": {
			"debug": {"echo_upstream_body": true}
		}
	}
}

This is an escape hatch — prefer the typed fields when one exists.