Get Started

First, you’ll need API keys from both OpenAI and Exa:

Python Example

Here’s a complete example using Python:

import json
from openai import OpenAI
from exa_py import Exa

OPENAI_API_KEY = ""  # Add your OpenAI API key here
EXA_API_KEY = ""     # Add your Exa API key here

def main():
    openai_client = OpenAI(api_key=OPENAI_API_KEY)
    exa = Exa(api_key=EXA_API_KEY)

    tools = [{
        "type": "function",
        "name": "exa_websearch",
        "description": "Search the web using Exa. Provide relevant links in your answer.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search query for Exa."
                }
            },
            "required": ["query"],
            "additionalProperties": False
        },
        "strict": True
    }]

    messages = [
        {"role": "system", "content": "You are a helpful assistant. Use exa_websearch to find info when relevant. Always list sources."},
        {"role": "user", "content": "Can you tell me the latest news about project stargate 2025?"}
    ]

    # Send initial request
    print("Sending initial request to OpenAI...")
    response = openai_client.responses.create(
        model="gpt-4o",
        input=messages,
        tools=tools
    )
    print("Initial OpenAI response:", response.output)

    # Check if the model returned a function call
    function_call = None
    for item in response.output:
        if item.type == "function_call" and item.name == "exa_websearch":
            function_call = item
            break

    # If exa_websearch was called
    if function_call:
        call_id = function_call.call_id
        args = json.loads(function_call.arguments)
        query = args.get("query", "")

        print(f"\nOpenAI requested a web search for: {query}")
        search_results = exa.search_and_contents(
            query=query,
            text=True,
            type="auto",
            livecrawl="always"
        )

        search_results_str = str(search_results)

        # Provide the function call + function_call_output to the conversation
        messages.append({
            "type": "function_call",
            "name": function_call.name,
            "arguments": function_call.arguments,
            "call_id": call_id
        })
        messages.append({
            "type": "function_call_output",
            "call_id": call_id,
            "output": search_results_str
        })

        print("\nSending search results back to OpenAI for a final answer...")
        response = openai_client.responses.create(
            model="gpt-4o",
            input=messages,
            tools=tools
        )

    # Print final answer text
    print("\nFinal Answer:\n", response.output_text)


if __name__ == "__main__":
    main()

JavaScript Example

Here’s the same example using JavaScript:

const OpenAI = require('openai');
const exaModule = require('exa-js');
const Exa = exaModule.default;

// Initialize API clients with API keys
const openai = new OpenAI({ apiKey: '' });  // Add your OpenAI API key here
const exa = new Exa('');                    // Add your Exa API key here

// Define websearch tool
const tools = [{
  type: 'function',
  name: 'exa_websearch',
  description: 'Search the web using Exa. Provide relevant links in your answer.',
  parameters: {
    type: 'object',
    properties: {
      query: {
        type: 'string',
        description: 'Search query for Exa.'
      }
    },
    required: ['query'],
    additionalProperties: false
  },
  strict: true,
}];

async function main() {
  const messages = [
    { role: 'system', content: 'You are a helpful assistant. Use exa_websearch to find info when relevant. Always list sources.' },
    { role: 'user', content: 'Can you tell me the latest news about project stargate 2025' }
  ];

  // Initial request to OpenAI
  console.log('Sending initial request to OpenAI...');
  let response = await openai.responses.create({
    model: 'gpt-4o',
    input: messages,
    tools
  });

  console.log('Initial OpenAI Response:', JSON.stringify(response, null, 2));

  // Check if the model wants to use the search function
  const functionCall = response.output.find(item => 
    item.type === 'function_call' && item.name === 'exa_websearch');

  if (functionCall) {
    const query = JSON.parse(functionCall.arguments).query;

    // Execute search with Exa API
    const searchResults = await exa.searchAndContents(query, {
      type: 'auto',
      livecrawl: 'always',
      text: true,
    });

    // Add function call and search results to the conversation
    messages.push(functionCall);
    messages.push({
      type: 'function_call_output',
      call_id: functionCall.call_id,
      output: JSON.stringify(searchResults)
    });

    // Send follow-up request to OpenAI with search results
    console.log('Sending follow-up request with search results to OpenAI...');
    response = await openai.responses.create({
      model: 'gpt-4o',
      input: messages,
      tools
    });
  }
  console.log('Final Answer:\n', response.output_text);
}

main();

Both examples show how to:

  1. Set up the OpenAI Response API with Exa as a tool
  2. Make a request to OpenAI
  3. Handle the search function call
  4. Send the search results back to OpenAI
  5. Get the final response

Remember to replace the empty API key strings with your actual API keys when trying these examples.

How Tool Calling Works

Let’s break down how the Exa web search tool works with OpenAI’s Response API:

  1. Tool Definition: First, we define our Exa search as a tool that OpenAI can use:

    {
      "type": "function",
      "name": "exa_websearch",
      "description": "Search the web using Exa...",
      "parameters": {
        "query": "string"  // The search query parameter
      }
    }
    
  2. Initial Request: When you send a message to OpenAI, the API looks at your message and decides if it needs to search the web. If it does, instead of giving a direct answer, it will return a “function call” in its output.

  3. Function Call: If OpenAI decides to search, it returns something like:

    {
      "type": "function_call",
      "name": "exa_websearch",
      "arguments": { "query": "your search query" }
    }
    
  4. Search Execution: Your code then:

    • Takes this search query
    • Calls Exa’s API to perform the actual web search
    • Gets real web results back
  5. Final Response: You send these web results back to OpenAI, and it gives you a final answer using the fresh information from the web.

This back-and-forth process happens automatically in the code above, letting OpenAI use Exa’s web search when it needs to find current information.