New to Exa? Try the Coding Agent Quickstart
to get started in under a minute.
OpenAI recommends the Responses API for all new projects. See the Responses API section below.
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
3
Add the Exa tools to your tool loop
Pass the tools in the request’s This is one round for brevity. A real agent keeps
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.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, and page text capped at 10,000 characters for contents.Responses API
For the OpenAI Responses API, use theresponses 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
Keyword arguments are regular Exa options, passed through when the tool runs — search options toexa.search(), contents options to exa.get_contents():
query and the urls to read; everything else is bound when you create the tool, so it can’t change what gets crawled or extracted.
name (defaulting to "web_search" and "get_contents") and description instead 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 anError: 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