search_provider to "exa" and Venice runs the search with Exa under zero data retention. You authenticate with your Venice API key, Venice bills the search, and you don’t need an Exa account.
Results come back in the same shape for every provider, so Exa slots into anything you’ve built on Venice’s default Brave search, and switching providers is a one-field change. This guide covers calling the endpoint, giving a Venice model Exa search as a tool, grounding a prompt in Exa results yourself, and when to call Exa’s own API instead.
Prerequisites
- A Venice API key
- Python 3.10 or later, or Node.js 22 or later
- For the agent example, a Venice model that supports function calling. The examples use
z-ai-glm-5-3-flash.
Setup
Install the OpenAI SDK, plusrequests for Python. Node.js has fetch built in:
Search with Exa
Send a query to/augment/search with search_provider set to "exa":
limit defaults to 10 and accepts 1 to 20, and query accepts 1 to 400 characters. Values outside those ranges return 400 rather than being clamped. Treat date as optional, since many pages don’t publish one.
Give a Venice model Exa search
To let a model decide when and what to search, describe aweb_search function in the tools array of a chat request, and run the search yourself whenever the model calls it. This program wraps the request above in a search helper, returns the results to the model as a tool message, and loops until the model answers in text:
- Truncate model-written queries. The endpoint rejects queries longer than 400 characters instead of shortening them, and models sometimes write long ones.
- Return errors as tool results. A failed search becomes a tool message starting with
Error:, so the model can try a different query or explain the failure instead of the loop crashing. - Append the assistant message before its tool results. Every
toolmessage has to follow the assistant turn that requested it. Appending the message object itself also keepsreasoning_content, which reasoning models like GLM 5.3 Flash return, so the model keeps its reasoning across rounds.
content is page text, up to about 10,000 characters, so the model can usually answer from the search results alone. When it needs a whole page, add a second tool that calls /augment/scrape, which returns the page as Markdown.
Search first, then generate
Function calling lets the model decide when and what to search. When your code already knows what to search, you can skip the tool loop: run one Exa search, number the results, and ask Venice to answer from them. This works with any Venice text model, and because you keep the list of sources that went into the prompt, every citation in the answer maps back to a URL. Using the search helper, client, and model from the previous example:"exa" to run it on Exa.
Use the Exa API directly
Venice’s search endpoint takes a query and a result count. Exa’s own API has more controls: categories for news, research papers, companies, people, and financial reports, domain and date filters, query-focused highlights and full page text, and deep multi-step search. To use them, call Exa directly with an API key from the Exa dashboard. Those requests go from your application to Exa, fall under Exa’s terms rather than Venice’s, and are billed to your Exa account. Exa offers Zero Data Retention on Enterprise plans. The Exa SDKs,exa-py and exa-js 2.24.0 or later, include tools in the Chat Completions format that drop into the loop above. Replace the tool list with Exa’s tools:
messages += exa.openai.handle_tool_calls(message) in Python, or messages.push(...(await exa.openai.handleToolCalls(message))) in Node.js. It runs each Exa call and returns the matching tool messages, with failures reported as Error: results. Keep the page text cap, because exa-js returns whole pages without one. Keyword arguments such as category, include_domains, and start_published_date pass through to Exa’s search. See Exa’s tool calling guide for every option.
Recommended models
Model IDs rotate. Resolve the current function-calling default at runtime with
GET /models/traits, or browse the text models for others that support function calling.
Privacy
Withsearch_provider: "exa", your application only talks to Venice, and Venice runs each search with Exa under zero data retention. Venice doesn’t store prompt or response content, and the model’s privacy tier decides what happens at the inference layer. Private models run on zero-data-retention infrastructure.
The part that’s easy to miss is that the model writes search queries from the conversation, so details from a prompt can end up in a query sent to Exa. If prompts may contain personal or confidential information, tell the model in your system prompt to keep those details out of searches, or use the search-first pattern so your own code writes every query.
If you call the Exa API directly, those requests go to Exa under Exa’s terms instead.
Costs
Each search costs $0.01, or $10 per 1,000, the same as a Brave or Google search. Venice bills it to your Venice account alongside the tokens your chat requests use, so there’s no separate Exa bill. Searches that fail with an error aren’t charged, but a search that comes back empty still is. Search results go back to the model as input on the next round, and because they carry page text, a five-result search often adds 5,000 to 10,000 input tokens. A few habits keep both down:- Lower
limitwhen a handful of sources is enough. A search costs the same at anylimit, but fewer results mean fewer input tokens on the next round. - Keep
max_roundslow. Each extra round can add searches as well as tokens. - Use the search-first pattern when your code knows what to search. It runs exactly one search and one chat request.
Troubleshooting
400 from the search endpoint
400 from the search endpoint
query must be 1 to 400 characters, limit must be 1 to 20, and search_provider must be brave, google, or exa. Values outside those ranges are rejected rather than clamped. In the tool loop, a rejected search reaches the model as an Error: tool result instead of raising.402 Payment Required
402 Payment Required
Your Venice balance can’t cover the request. Requests made with an API key return
INSUFFICIENT_BALANCE, so top up your Venice balance. Requests made with x402 wallet authentication return top-up instructions in the response.400 from Venice after a tool round
400 from Venice after a tool round
Some models, including Claude and GPT models, return
400 when a tool call isn’t followed by a tool message with its tool_call_id. GLM models accept the conversation anyway, so this can first appear when you switch models. The loop answers every call right after the assistant message that made it, so keep that shape if you add tools.The model answers without searching
The model answers without searching
Check that the model supports function calling, and say when to search in the system prompt. Models often skip the tool when they think they can answer from memory, unless you tell them to search for recent or specific facts.
Resources
Web Search API
Parameters, search providers, and the response schema
Cited Answers
A full search, scrape, and cite pipeline on Venice
Function Calling
How tool calls work on Venice
Exa Docs
Exa’s own API, SDKs, and search options