
Call custom functions on the Grok API
Call custom functions on the Grok API
Define tools with a name, description, and JSON Schema. The model returns a tool_call (or Responses function_call) when it needs your side. You run the function locally, send the result back, and the model continues. Cap is 350 tools per request. Root parameters must be an object (or a oneOf/anyOf of objects); a scalar or array root returns 400.
Minimal Responses call
curl https://api.x.ai/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"model": "grok-4.6",
"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"]
}
}
]
}'
Handle the call (OpenAI SDK)
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"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"},
},
"required": ["location"],
},
}]
response = client.responses.create(
model="grok-4.6",
input=[{"role": "user", "content": "What is the temperature in San Francisco?"}],
tools=tools,
)
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
result = {"location": args["location"], "temperature": 59, "unit": args.get("unit", "fahrenheit")}
response = client.responses.create(
model="grok-4.6",
input=[{"type": "function_call_output", "call_id": item.call_id, "output": json.dumps(result)}],
tools=tools,
previous_response_id=response.id,
)
for item in response.output:
if item.type == "message":
print(item.content[0].text)
Tool choice and parallel calls
tool_choice |
Behavior |
|---|---|
"auto" (default) |
Model decides |
"required" |
Must call at least one tool |
"none" |
Disable tools |
{"type": "function", "function": {"name": "…"}} |
Force one tool |
Parallel calls are on by default — process every tool_call before the next sample. Set parallel_tool_calls: false to force one at a time.
Mix with built-in tools
You can pass web_search / x_search (server-side) next to your custom functions (client-side). Built-ins run on xAI; custom tools pause for your handler. With streaming, each function call arrives whole in one chunk.
Pitfalls
- Streaming expecting partial function-call chunks — the call arrives complete in a single chunk.
- A
parametersschema whose root is a string, number, or array — rejected with400naming the tool. - Returning only one result when the model requested several parallel calls.