Skip to main content
Lesson 11 of 16 · Responses API: Primary Endpoint

Custom functions (function calling)

Lesson 11 of 16 Updated July 30, 2026 Developer
On this page
  1. Connect Grok to your own services
  2. How it works
  3. Declare a function
  4. Receive the function call
  5. Return the result
  6. Best practices for descriptions
  7. Error handling
  8. Key takeaways

Connect Grok to your own services

Server tools (web_search, x_search, code_interpreter) are handy but limited to xAI services. With custom function tools, you can connect Grok to any API or service in your infrastructure.

How it works

Function calling follows a three-step cycle:

  1. Declaration: You describe your functions (name, description, parameters) in the request
  2. Call: The model generates a function call with the appropriate arguments and pauses generation
  3. Result: Your code executes the function, then you send the result back to the model so it can formulate its response

Declare a function

{
  "model": "grok-4.20-0309-reasoning",
  "input": "Book me a table for 2 people tonight at 8 PM.",
  "tools": [{
    "type": "function",
    "name": "book_restaurant",
    "description": "Books a table at a restaurant",
    "parameters": {
      "type": "object",
      "properties": {
        "restaurant_name": {
          "type": "string",
          "description": "Restaurant name"
        },
        "date": {
          "type": "string",
          "description": "Date in YYYY-MM-DD format"
        },
        "time": {
          "type": "string",
          "description": "Time in HH:MM format"
        },
        "guests": {
          "type": "integer",
          "description": "Number of guests"
        }
      },
      "required": ["date", "time", "guests"]
    }
  }]
}

The description of each function and parameter is crucial. The model uses them to decide when to call the function and how to fill the arguments.

Receive the function call

When the model decides to call your function, the response contains a function_call item:

{
  "id": "resp-xyz789",
  "status": "completed",
  "output": [{
    "type": "function_call",
    "name": "book_restaurant",
    "arguments": "{\"date\": \"2026-04-03\", \"time\": \"20:00\", \"guests\": 2}",
    "call_id": "call-abc123"
  }]
}

The arguments field is a JSON string that you must parse. The call_id identifies this specific call.

Return the result

After executing the function in your code, return the result:

import json

# 1. Initial request
response = client.responses.create(
    model="grok-4.20-0309-reasoning",
    input="What's the weather like in Paris?",
    tools=[{
        "type": "function",
        "name": "get_weather",
        "description": "Retrieves current weather",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    }]
)

# 2. Check if the model wants to call a function
for item in response.output:
    if item.type == "function_call":
        args = json.loads(item.arguments)

        # 3. Execute your function
        weather_data = get_weather_from_api(args["city"])

        # 4. Return the result to the model
        final = client.responses.create(
            model="grok-4.20-0309-reasoning",
            input=[{
                "type": "function_call_output",
                "call_id": item.call_id,
                "output": json.dumps(weather_data)
            }],
            previous_response_id=response.id
        )
        print(final.output[0].content[0].text)

Best practices for descriptions

The model relies on descriptions to understand your functions. Be precise:

{
  "name": "search_products",
  "description": "Searches for products in the e-commerce catalog. Returns the top 10 results sorted by relevance. Supports search by name, category, or price range.",
  "parameters": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "Search terms (product name, brand, keyword)"
      },
      "category": {
        "type": "string",
        "enum": ["electronics", "clothing", "home", "sports"],
        "description": "Product category to filter"
      },
      "max_price": {
        "type": "number",
        "description": "Maximum price in euros"
      }
    },
    "required": ["query"]
  }
}

Use enum for predefined values, detailed description for each parameter, and mark only essential parameters as required.

Error handling

If your function fails, return the error as the result:

try:
    result = call_external_api(args)
    output = json.dumps(result)
except Exception as e:
    output = json.dumps({"error": str(e)})

final = client.responses.create(
    model="grok-4.20-0309-reasoning",
    input=[{
        "type": "function_call_output",
        "call_id": call_id,
        "output": output
    }],
    previous_response_id=response.id
)

The model interprets the error and can rephrase its response or try a different approach.

Key takeaways

  • Custom functions connect Grok to your own services
  • The cycle is: declare, receive the call, execute, return the result
  • Function and parameter descriptions guide the model’s choice
  • The arguments field is a JSON string to parse on the client side
  • Return errors as results so the model can adapt