Implement function/tool calling with OpenRouter models. Use when building agents or structured outputs. Trigger with phrases like 'openrouter functions', 'openrouter tools', 'openrouter agent', 'function calling'.
OpenRouter supports OpenAI-compatible tool/function calling across multiple providers. Define tools as JSON Schema, send them with your request, and the model returns structured tool_calls instead of free text. This works with GPT-4o, Claude 3.5, Gemini, and other tool-capable models via the same API. The key difference from direct provider APIs: OpenRouter normalizes the tool calling interface, so the same code works across providers.
Prerequisites
An OpenRouter API key (sk-or-v1-...) exported as OPENROUTER_API_KEY — see the openrouter-install-auth skill for setup
Python 3.8+ or Node.js 18+ with the OpenAI SDK (pip install openai / npm install openai)
A tool-capable model — check the Model Compatibility table below or query /api/v1/models (e.g., openai/gpt-4o, )
anthropic/claude-3.5-sonnet
Real function implementations to dispatch tool calls to (the execute_tool() dispatcher below stubs get_weather and search_database)
Instructions
Pick a model from the Model Compatibility table that supports the features you need (tool calling, JSON mode, parallel tools).
Define your tools as JSON Schema per Basic Tool Calling and send them with tool_choice="auto" (or "required" to force a call, or a specific function name).
Read response.choices[0].message.tool_calls — each entry carries function.name and JSON-encoded function.arguments to parse with json.loads().
For agents, wire the Multi-Turn Tool Loop: append the assistant message, execute each tool via execute_tool(), append role: "tool" results keyed by tool_call_id, and loop until the model returns plain text (bounded by max_rounds).
Use the TypeScript Tool Calling section for the identical flow in Node — same schema, same tool_calls shape.
When you only need structured data (no function execution), skip tools and use Structured Output (JSON Mode) with response_format={"type": "json_object"}.
Handle failures per the Error Handling table: force tool_choice: "required" for extraction pipelines and validate arguments server-side before executing.
# Force JSON output without tool calling (simpler for extraction tasks)
response = client.chat.completions.create(
model="openai/gpt-4o",
messages=[
{"role": "system", "content": "Extract data as JSON with fields: name, email, company"},
{"role": "user", "content": "Contact Jane Smith at [email protected], she works at Acme Corp"},
],
response_format={"type": "json_object"},
max_tokens=200,
)
data = json.loads(response.choices[0].message.content)
# → {"name": "Jane Smith", "email": "[email protected]", "company": "Acme Corp"}
Model Compatibility
Model
Tool Calling
JSON Mode
Parallel Tools
openai/gpt-4o
Yes
Yes
Yes
openai/gpt-4o-mini
Yes
Yes
Yes
anthropic/claude-3.5-sonnet
Yes
Via system prompt
Sequential
google/gemini-2.0-flash-001
Yes
Yes
Yes
meta-llama/llama-3.1-70b-instruct
Yes (varies)
Via prompt
No
Output
The tool-calling flows produce:
message.tool_calls entries — each with a function.name and JSON-encoded function.arguments (e.g., get_weather with {"location": "Tokyo", "unit": "celsius"}) plus a tool_call_id for pairing results
The final assistant text once the Multi-Turn Tool Loop resolves — or the "Max tool rounds exceeded" sentinel if it hits max_rounds
From JSON Mode: a parseable JSON object matching your system-prompt schema (e.g., {"name": "Jane Smith", "email": "[email protected]", "company": "Acme Corp"})
Examples
Asking a weather question with the get_weather tool registered:
message = response.choices[0].message
for tc in message.tool_calls:
print(tc.function.name, json.loads(tc.function.arguments))
# get_weather {'location': 'Tokyo', 'unit': 'celsius'}
Feed that result back as a role: "tool" message and the next completion returns prose ("It's currently 22°C and sunny in Tokyo..."). More worked examples: references/examples.md.
Error Handling
Error
Cause
Fix
tool_calls is null
Model chose not to call tools
Use tool_choice: "required" to force tool use
JSON parse error on arguments
Model generated malformed JSON
Wrap in try/catch; retry or use more capable model
400 invalid tool schema
Unsupported JSON Schema types
Stick to basic types (string, number, boolean, object, array)
Tool called with wrong args
Schema description unclear
Improve parameter descriptions; add examples in description
Enterprise Considerations
Not all models support tool calling -- check model capabilities via /api/v1/models before sending tools
Use tool_choice: "required" when you must get a tool call (e.g., extraction pipelines)
Validate tool arguments server-side before executing -- models can hallucinate argument values
Set max_tokens to prevent expensive completion when model decides not to use tools
Use fallback chain with tool-capable models only (see openrouter-fallback-config)
Log tool call names and arguments for audit trails (redact sensitive args)