Integrate with the ChatOpenRouter chat model using LangChain Python.
This will help you get started with OpenRouter chat models. OpenRouter is a unified API that provides access to models from multiple providers (OpenAI, Anthropic, Google, Meta, and more) through a single endpoint.
API ReferenceFor detailed documentation of all features and configuration options, head to the ChatOpenRouter API reference.
messages = [ ( "system", "You are a helpful assistant that translates English to French. Translate the user sentence.", ), ("human", "I love programming."),]ai_msg = model.invoke(messages)ai_msg.content
OpenRouter uses the OpenAI-compatible tool calling format. You can describe tools and their arguments, and have the model return a JSON object with a tool to invoke and the inputs to that tool.
With ChatOpenRouter.bind_tools, you can pass in Pydantic classes, dict schemas, LangChain tools, or functions as tools to the model. Under the hood these are converted to OpenAI tool schemas and passed in every model invocation.
Copy
from pydantic import BaseModel, Fieldclass GetWeather(BaseModel): """Get the current weather in a given location""" location: str = Field(..., description="The city and state, e.g. San Francisco, CA")model_with_tools = model.bind_tools([GetWeather])
Copy
ai_msg = model_with_tools.invoke( "what is the weather like in San Francisco",)ai_msg
ChatOpenRouter supports structured output via the with_structured_output method. Two methods are available: function_calling (default) and json_schema.
Individual model calls
Use with_structured_output to generate a structured model response. Specify method="json_schema" to use JSON Schema-based structured output; otherwise the method defaults to function calling.
Copy
from langchain_openrouter import ChatOpenRouterfrom pydantic import BaseModel, Fieldmodel = ChatOpenRouter(model="openai/gpt-4.1")class Movie(BaseModel): """A movie with details.""" title: str = Field(..., description="The title of the movie") year: int = Field(..., description="The year the movie was released") director: str = Field(..., description="The director of the movie") rating: float = Field(..., description="The movie's rating out of 10")structured_model = model.with_structured_output(Movie, method="json_schema") response = structured_model.invoke("Provide details about the movie Inception")response
Specify response_format with ProviderStrategy to engage structured output when generating the agent’s final response.
Copy
from langchain.agents import create_agentfrom langchain.agents.structured_output import ProviderStrategyfrom pydantic import BaseModelclass Weather(BaseModel): temperature: float condition: strdef weather_tool(location: str) -> str: """Get the weather at a location.""" return "Sunny and 75 degrees F."agent = create_agent( model="openrouter:openai/gpt-4.1", tools=[weather_tool], response_format=ProviderStrategy(Weather), )result = agent.invoke({ "messages": [{"role": "user", "content": "What's the weather in SF?"}]})result["structured_response"]
Copy
Weather(temperature=75.0, condition='Sunny')
You can pass strict=True with the function_calling and json_schema methods to enforce exact schema adherence. The strict parameter is not supported with json_mode.
For models that support reasoning (e.g., anthropic/claude-sonnet-4.5, deepseek/deepseek-r1), you can enable reasoning tokens via the reasoning parameter. See the OpenRouter reasoning docs for details:
Copy
model = ChatOpenRouter( model="anthropic/claude-sonnet-4.5", max_tokens=16384, reasoning={"effort": "high", "summary": "auto"},)ai_msg = model.invoke("What is the square root of 529?")# Access reasoning content via content_blocksfor block in ai_msg.content_blocks: if block["type"] == "reasoning": print(block["reasoning"])
For more on content blocks, see the standard content blocks guide.The reasoning dict supports two keys:
The effort-to-budget mapping is model-dependent. For example, Google Gemini models map effort to an internal thinkingLevel rather than an exact token budget. See the OpenRouter reasoning docs for details.
OpenRouter supports multimodal inputs for models that accept them. The available modalities depend on the model you select — check the OpenRouter models page for details.
When the underlying provider includes detailed token breakdowns in its response, they are surfaced automatically. These fields are omitted when the provider does not report them or when their values are zero.
output_token_details.reasoning reports the number of tokens the model used for internal chain-of-thought reasoning. This appears when using reasoning models (e.g., deepseek/deepseek-r1, openai/o3) or when reasoning is explicitly enabled:
Copy
from langchain_openrouter import ChatOpenRoutermodel = ChatOpenRouter( model="anthropic/claude-sonnet-4.5", reasoning={"effort": "high"},)ai_msg = model.invoke("What is the square root of 529?")ai_msg.usage_metadata
input_token_details.cache_read reports the number of input tokens served from the provider’s prompt cache, and input_token_details.cache_creation reports tokens written to the cache on the first call.Prompt caching requires explicit cache_control breakpoints in message content blocks. Pass {"cache_control": {"type": "ephemeral"}} on the content block you want cached:
Copy
from langchain_openrouter import ChatOpenRoutermodel = ChatOpenRouter(model="anthropic/claude-sonnet-4.5")long_system = "You are a helpful assistant. " * 200messages = [ ("system", [{"type": "text", "text": long_system, "cache_control": {"type": "ephemeral"}}]), ("human", "Say hi."),]# First call writes to cacheai_msg = model.invoke(messages)ai_msg.usage_metadata
The native_finish_reason field, if present, contains the underlying provider’s original finish reason, which may differ from the normalized finish_reason.
Many models on OpenRouter are served by multiple providers. The openrouter_provider parameter gives you control over which providers handle your requests and how they’re selected.
Use order to set a preferred provider sequence. OpenRouter tries each provider in order and falls back to the next if one is unavailable:
Copy
model = ChatOpenRouter( model="anthropic/claude-sonnet-4.5", openrouter_provider={ "order": ["Anthropic", "Google"], "allow_fallbacks": True, # default; fall back beyond the order list if needed },)
To restrict requests to specific providers only, use only. To exclude certain providers, use ignore:
Copy
# Only use these providers (no fallback to others)model = ChatOpenRouter( model="openai/gpt-4o", openrouter_provider={"only": ["OpenAI", "Azure"]},)# Use any provider except DeepInframodel = ChatOpenRouter( model="meta-llama/llama-4-maverick", openrouter_provider={"ignore": ["DeepInfra"]},)
model = ChatOpenRouter( model="openai/gpt-4o", openrouter_provider={ "order": ["OpenAI", "Azure"], "allow_fallbacks": False, # strict — only use providers in order "require_parameters": True, # skip providers that don't support all params "data_collection": "deny", },)
OpenRouter supports app attribution via HTTP headers. You can set these through init params or environment variables:
Copy
model = ChatOpenRouter( model="anthropic/claude-sonnet-4.5", app_url="https://myapp.com", # or OPENROUTER_APP_URL env var app_title="My App", # or OPENROUTER_APP_TITLE env var)
For detailed documentation of all ChatOpenRouter features and configurations, head to the ChatOpenRouter API reference.For more information about OpenRouter’s platform, models, and features, see the OpenRouter documentation.