Skip to main content
A custom tool (type: function) lets the agent call code you control - your database, an internal API, or any business logic. You declare the function with a name, a description, and a JSON Schema for its parameters. The model decides when to call it and fills in the arguments; you run the function and return the result so the run can continue. Unlike built-in tools, the Agent API never executes your code. When the model calls a custom function, the run pauses and hands the call back to you.

Define a custom tool

Declare the tool in the tools array. Set strict: true to enforce the parameter schema.
from perplexity import Perplexity

client = Perplexity()

tools = [
    {
        "type": "function",
        "name": "get_order_status",
        "description": "Look up the current status of an internal order by its order ID.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
        "strict": True,
    }
]

response = client.responses.create(
    model="openai/gpt-5.6-sol",
    input="What's the status of order ORD-10042?",
    tools=tools,
)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const tools = [
  {
    type: 'function' as const,
    name: 'get_order_status',
    description: 'Look up the current status of an internal order by its order ID.',
    parameters: {
      type: 'object',
      properties: { order_id: { type: 'string' } },
      required: ['order_id'],
    },
    strict: true,
  },
];

const response = await client.responses.create({
  model: 'openai/gpt-5.6-sol',
  input: "What's the status of order ORD-10042?",
  tools,
});
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.6-sol",
    "input": "What is the status of order ORD-10042?",
    "tools": [
      {
        "type": "function",
        "name": "get_order_status",
        "description": "Look up the current status of an internal order by its order ID.",
        "parameters": {
          "type": "object",
          "properties": { "order_id": { "type": "string" } },
          "required": ["order_id"]
        },
        "strict": true
      }
    ]
  }' | jq

Read the function call

The run pauses and returns the call in the response output array as a function_call item with the name and the arguments the model filled in:
{
  "type": "function_call",
  "id": "fc_abc123",
  "call_id": "call_xyz789",
  "name": "get_order_status",
  "arguments": "{\"order_id\":\"ORD-10042\"}"
}

Return the result

Pull that item off the response, run the function on your side, then continue the run by sending the result back. The follow-up request replays the conversation so far - the original question, the function_call the model emitted, and a function_call_output carrying your result under the same call_id:
import json

function_call = next(item for item in response.output if item.type == "function_call")

def get_order_status(order_id: str) -> dict:
    orders = {"ORD-10042": {"status": "in_transit", "carrier": "DHL", "eta": "2026-06-23"}}
    return orders.get(order_id, {"error": "order not found"})

result = get_order_status(**json.loads(function_call.arguments))

followup = client.responses.create(
    model="openai/gpt-5.6-sol",
    input=[
        {"role": "user", "content": "What's the status of order ORD-10042?"},
        {
            "type": "function_call",
            "call_id": function_call.call_id,
            "name": function_call.name,
            "arguments": function_call.arguments,
        },
        {
            "type": "function_call_output",
            "call_id": function_call.call_id,
            "output": json.dumps(result),
        },
    ],
    tools=tools,
)

print(followup.output_text)
const functionCall = response.output.find((item) => item.type === 'function_call');
if (functionCall?.type !== 'function_call') throw new Error('No function call returned.');

function getOrderStatus(orderId: string) {
  const orders: Record<string, unknown> = {
    'ORD-10042': { status: 'in_transit', carrier: 'DHL', eta: '2026-06-23' },
  };
  return orders[orderId] ?? { error: 'order not found' };
}

const args = JSON.parse(functionCall.arguments) as { order_id: string };
const result = getOrderStatus(args.order_id);

const followup = await client.responses.create({
  model: 'openai/gpt-5.6-sol',
  input: [
    { type: 'message', role: 'user', content: "What's the status of order ORD-10042?" },
    {
      type: 'function_call',
      call_id: functionCall.call_id,
      name: functionCall.name,
      arguments: functionCall.arguments,
    },
    {
      type: 'function_call_output',
      call_id: functionCall.call_id,
      output: JSON.stringify(result),
    },
  ],
  tools,
});

console.log(followup.output_text);
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.6-sol",
    "input": [
      { "role": "user", "content": "What is the status of order ORD-10042?" },
      {
        "type": "function_call",
        "call_id": "call_xyz789",
        "name": "get_order_status",
        "arguments": "{\"order_id\":\"ORD-10042\"}"
      },
      {
        "type": "function_call_output",
        "call_id": "call_xyz789",
        "output": "{\"status\": \"in_transit\", \"carrier\": \"DHL\", \"eta\": \"2026-06-23\"}"
      }
    ]
  }' | jq
call_id is the correlation key: the function_call the model emits and the function_call_output you return must carry the same value. Set strict: true on the tool to enforce the parameter schema. You continue the run by replaying the prior items in the input array - pass the function_call and its function_call_output together so the model sees its own request and your result.

Next steps

Give it tools

The end-to-end how-to for wiring built-in, MCP, and custom tools into a run.

Tools overview

Every built-in tool, plus MCP servers and custom functions.