Skip to main content
OpenAI recommends the Responses API for all new projects. See the Responses API section below.
OpenAI’s tool calling allows models to call functions that you define in your code. The Exa SDKs ship ready-made web search and page reading tools for OpenAI, so you don’t have to hand-write the tool schema, parse tool calls, or format Exa results yourself.

Get started

1

Install the SDKs

2

Set up your API keys

Set the EXA_API_KEY and OPENAI_API_KEY environment variables. Visit the OpenAI dashboard and the Exa dashboard to generate your API keys.

Get your Exa API key

Create a key in the dashboard. New accounts start with free credits.
3

Add the Exa tools to your tool loop

Pass the tools in the request’s tools list, then hand the assistant message to handle_tool_calls. It executes every Exa tool call in the message and returns the matching role: "tool" messages, ready to append to the conversation.web_search searches the web for pages the model hasn’t seen; get_contents reads pages it already has URLs for, whether from an earlier search or from the user. Register either or both.
This is one round for brevity. A real agent keeps tools on every request and repeats the handler step until the model replies without tool calls — that’s how a search result turns into a follow-up page read.Calling the factories with no arguments gives Exa’s recommended defaults: type="auto" with contents={"highlights": True} for search. Highlights return query-relevant excerpts — they do not cap page text at 10,000 characters. The contents factory returns page text; the SDK’s 10,000-character limit applies only to text, and only when you omit max_characters.

Responses API

For the OpenAI Responses API, use the responses factory with the same handle_tool_calls helper. The handler returns function_call_output items for a follow-up request.
Chat Completions and the Responses API use different tool shapes and reject each other’s, so use the factory that matches the endpoint you’re calling.

Configuring the tools

Call the factories with no arguments to get Exa’s recommended defaults:
  • web_search runs exa.search(query, type="auto", num_results=10, contents={"highlights": True}) — query-relevant excerpts from each result.
  • get_contents runs exa.get_contents(urls) — page text, capped at 10,000 characters by the Python SDK by default.
Any keyword argument you pass to a factory overrides these — search options go to exa.search(), contents options to exa.get_contents() — but we recommend the defaults.
The model picks the search query and objective and the urls to read; everything else is fixed when you create the tool, so it can’t change what gets crawled or extracted. Read more about the objective parameter. name (defaulting to "web_search" and "get_contents") and description override the tool definition the model sees. Use a custom name to run differently-configured Exa tools side by side, or to avoid clashes with other tools that reserve those names.

Mixing in your own tools

The handlers answer every tool call in the message: a call naming a tool they can’t resolve gets an Error: unknown tool "<name>" output instead of being dropped, so the follow-up request never omits a required tool response. If you run your own tools alongside Exa’s, replace those error outputs with your own results before the next request.

Writing the loop by hand

If you’d rather own the tool schema and execution yourself, define the tool and process the calls manually. exa.tools.web_search() and exa.tools.get_contents() give you the same provider-neutral tool specs (with a run method) for hand-rolled loops, or you can write everything from scratch:
Python
See the SDK quickstart for search and contents options in Python and TypeScript.
Last modified on October 2, 2026