
Force a specific tool with tool_choice
Make the model call a named function (or require any tool) on this Responses turn when “maybe later” is the wrong outcome for your workflow. Official Function Calling documents the tool_choice request field with four shapes: "auto" (default — the model decides), "required" (at least one tool call), "none" (disable tools for this turn), and {"type": "function", "function": {"name": "..."}} to force one specific tool by name. Pair this with tools you already defined in tools[]. Create a key and load credits at console.x.ai.
What you need
An XAI_API_KEY, at least one custom function tool (name, description, JSON Schema parameters with an object root), and a product rule that needs deterministic tool use — for example always logging a ticket before the assistant answers, or disabling tools on a pure-summarize turn. Neighboring jobs include Call a function with the Grok API for the define-and-return loop, Use the image generation tool in a conversation when the server-side Imagine tool is in the same request, and Configure image generation tool action for gating generate vs edit. More API jobs live on the API hub.
Set tool_choice on the request
- Export the key:
export XAI_API_KEY="your_api_key"
- Force a named function by passing the object form of
tool_choicealongside yourtoolsarray. The name must match a tool you listed:
curl https://api.x.ai/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"model": "grok-4.7",
"input": [
{"role": "user", "content": "What is the temperature in San Francisco?"}
],
"tools": [
{
"type": "function",
"name": "get_temperature",
"description": "Get current temperature for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"}
},
"required": ["location"]
}
}
],
"tool_choice": {
"type": "function",
"function": {"name": "get_temperature"}
}
}'
Require any tool without naming one by setting
"tool_choice": "required", or silence tools for this turn with"tool_choice": "none"while still sending the sametoolsdefinitions for later turns that reuseprevious_response_id.When the model returns multiple client-side calls in one response (parallel function calling is enabled by default), execute every call before you continue — official docs show iterating
response.tool_calls/function_calloutput items and appending each result. Disable parallelism for this request with"parallel_tool_calls": falsewhen your backend cannot handle concurrent tool work safely.
import json
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("XAI_API_KEY"), base_url="https://api.x.ai/v1")
tools = [
{
"type": "function",
"name": "get_temperature",
"description": "Get current temperature for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "fahrenheit",
},
},
"required": ["location"],
},
}
]
response = client.responses.create(
model="grok-4.7",
input=[{"role": "user", "content": "What is the temperature in San Francisco?"}],
tools=tools,
tool_choice={"type": "function", "function": {"name": "get_temperature"}},
parallel_tool_calls=False,
)
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
# Run your real function, then send function_call_output with item.call_id
print(item.name, args)
Mixing with built-in tools
tool_choice applies to the whole turn. Built-in server-side tools (web search, X search, and similar) still execute on xAI’s side when the model selects them; custom type: "function" tools still pause for your local execution. Forcing a custom function name does not run that function for you — you still handle the function_call / tool_call and return function_call_output / tool_result before the model can finish the answer.
Pitfalls
Forcing a name that is missing from tools[] fails validation or yields an empty/error tool path — keep the catalogs identical. Using "required" without any tools defined cannot satisfy the constraint. Leaving parallel calls enabled while your handler only processes the first function_call drops sibling results and confuses the next turn. Parameter schemas still need an object root (or a oneOf/anyOf of objects); a scalar root is rejected with 400 regardless of tool_choice.