Web search in Open WebUI with a Brave or Tavily API key

Set up Open WebUI web search with a real search API: Brave or Tavily keys, the model settings that let it search, page-size limits, and what a search costs.

· ranx.cloud

Illustration of a brass magnifying glass resting on a folded newspaper next to a pair of reading glasses

This is for anyone whose Open WebUI answers questions about last week with confident guesses from the model's training data. You end up with web search backed by a search API you hold the key for (Brave or Tavily), models that decide for themselves when to search and which pages to read, a cap on how much of each page goes into the prompt, and a clear idea of what one searched answer costs. Everything is configured in the web interface.

Settings below were checked against Open WebUI v0.13.0. Prices are as of November 2026.

How search works in this version

There are two jobs: a search engine returns a list of links with snippets, and a web loader opens a page and extracts its text. Open WebUI ships with dozens of engines; you pick one.

Since v0.10, tool calling runs in Native mode by default, and that changes how search behaves. Turning on web search in a chat doesn't run a search before every answer. It hands the model two tools:

  • search_web returns titles, links and snippets from your engine. Nothing is chunked or stored.
  • fetch_url reads one page in full through the web loader and puts its text into the model's context.

The model decides whether to search, reads the snippets, fetches the pages worth reading, and may search again. Only fetched pages appear as sources under the answer; search hits alone are not cited (agentic search docs). This needs a model that calls tools reliably, but not an expensive one: the docs list small, fast models as meeting the bar.

Step 1: Pick a search API and get a key

Brave Search API Tavily
Sign up api-dashboard.search.brave.com app.tavily.com
Free allowance $5 of credits each month 1,000 credits each month
Paid $5 per 1,000 requests $0.008 per credit
Per search 1 request 1 credit at basic depth, 2 at advanced
Engines in Open WebUI brave, brave_llm_context tavily

Sources: Brave Search API and Tavily credits, November 2026.

Both are built for this kind of use, and both cover a personal instance on the free allowance: Brave's $5 is 1,000 searches a month, about 45 per working day. Brave has its own index and two endpoints. brave_llm_context returns extracted, relevance-scored passages from each page as the snippet, so the model often has enough to answer without fetching pages at all. Tavily returns cleaned results aimed at LLMs and also offers page extraction.

Open WebUI also has a keyless DuckDuckGo option (DDGS). It works until DuckDuckGo starts rate-limiting the instance's address, which is why the docs recommend a hosted API for dependable results.

Step 2: Configure the engine

Open Settings (click your avatar at the bottom of the sidebar), go to the Admin section and open Web Search, under Tools.

  1. Switch Web Search on.
  2. Web Search Engine: brave_llm_context (or brave, or tavily).
  3. Paste the key into Brave Search API Key or Tavily API Key.
  4. Under Search Limits, set Search Result Count to 5 and Concurrent Requests to 1.
  5. Set Fetch URL Content Length Limit to 20000.
  6. For brave_llm_context, also switch on Bypass Web Loader, so Open WebUI uses Brave's passages as they are instead of scraping each result page again.
  7. Save.

Why those numbers:

  • Search Result Count is the maximum the model gets per search; the model can ask for fewer, never more. Five snippets is enough to choose what to read.
  • Concurrent Requests 1 runs a search's queries one after another. Brave rejects bursts with HTTP 429, and Open WebUI retries only once. On a single-user instance it costs nothing noticeable.
  • Fetch URL Content Length Limit caps how many characters of a fetched page reach the model. Without it, the whole page goes in. 20,000 characters is roughly 5,000 tokens of English, enough for an article or a docs page. A longer page is cut and ends with [Content truncated...].

With Tavily, Tavily Search Depth appears. Leave it on Basic (1 credit); Advanced costs 2.

Domain Filter List takes a comma-separated list. A domain with ! in front is blocked: !pinterest.com,!quora.com. Careful: any entry without ! turns the list into an allowlist, and then only those domains are searched.

Step 3: Let the models use it

Three switches must all be on for a model to search. The global default covers every model, so set them once in Models (Admin section, under AI), via Model Defaults at the top of the list:

  • Capabilities → Web Search: the model may search.
  • Capabilities → Builtin Tools, with its Web Search category on. In Native mode, search is a built-in tool; with Builtin Tools off the model gets no way to search at all (#32367).
  • Default Features → Web Search: optional. When on, search starts enabled in every new chat. Leave it off if you'd rather switch it on per chat.

Under Model Parameters, Function Calling should be Default or Native, not Legacy.

In a chat, the Integrations button in the message box holds the Web Search toggle. With it on, ask normally: "What changed in the latest Node.js LTS release?" The status line shows the searches and fetches as they happen, and the sources list under the answer links the pages the model read.

If a particular model should never search (a writing preset, say), switch its Web Search capability off in that model's own settings; per-model values win over Model Defaults.

What one searched answer costs

Search cost has two parts: the search API, and the tokens the results add. Tokens usually dominate, because in a tool loop the model receives the whole conversation again after every tool call.

A typical research question, assuming a 4,000-token start (system prompt, tool definitions, your question), one search returning about 1,500 tokens of snippets, two page fetches capped at about 5,000 tokens each, and a 700-token answer:

round 1  (decide to search)          4,000 input
round 2  (+ snippets, fetch page 1)  5,600 input
round 3  (+ page 1, fetch page 2)   10,700 input
round 4  (+ page 2, answer)         15,800 input, 700 output
total                               36,100 input, ~1,000 output

At November 2026 prices (Anthropic, OpenRouter):

Model Tokens Search API Per answer
Claude Sonnet 5.5 ($2 / $10) 36,100 × $2/M + 1,000 × $10/M = $0.082 $0.005 ≈ $0.087
DeepSeek V4.1 Flash ($0.0486 / $0.54) 36,100 × $0.0486/M + 1,000 × $0.54/M = $0.0023 $0.005 ≈ $0.0073

Ten searched questions a working day comes to about $19 a month on Sonnet 5.5 and about $1.60 on DeepSeek V4.1 Flash, against a search bill covered by Brave's free credits either way. Without the content cap, one long page can be 30,000 tokens on its own, and it's then resent in every following round.

Claude through Anthropic's OpenAI-compatible endpoint gets no prompt caching, so each round pays full price for the rounds before it. OpenAI and DeepSeek cache repeated prefixes automatically, which softens the growth.

A practical split: a cheap model as the default for "look this up" chats, and a stronger one when you need judgment about what you found.

Legacy mode and small instances

Older or weaker models that don't call tools reliably can use Legacy function calling instead. Then Open WebUI searches before every answer, using the task model to write the queries, fetches every result, chunks the pages, and embeds them to pick the passages that fit your question.

That embedding step is the part to watch on a small instance. By default it uses a local embedding model inside the Open WebUI process, which needs around 500 MB of memory on its own according to Open WebUI's docs. On a 2 GB plan, switch on Bypass Embedding and Retrieval on the Web Search page: the fetched text then goes to the model directly, with no embedding. With the content length limit set, that stays within budget. Native mode never embeds search results, which is another reason to prefer it.

What goes wrong

The toggle is on, but the model answers without searching. In Native mode the model decides, and it may decide its training data is enough (#33773). Ask explicitly ("search the web for…"), or switch to a model that calls tools more readily. Check the three switches in step 3 if it never searches.

HTTP 429 errors from Brave. Too many requests at once. Set Concurrent Requests to 1.

Answers cite pages, but the content is empty or wrong. Some sites block automated readers or render their text with JavaScript. The default loader reads plain HTML. Try another source, or with Tavily or Brave LLM context rely on their extracted text.

Searches only ever return the same few sites. The Domain Filter List has an entry without !, so it became an allowlist.

No sources listed under an answer that clearly used the web. The model answered from snippets and didn't fetch a page. In Native mode only fetched pages are cited.

Gemini models return empty replies when searching. Google's OpenAI-compatible endpoint breaks tool calls in streaming (#28492). Use Gemini through OpenRouter for search chats.

A long research chat suddenly gets expensive. Every fetched page stays in the history and is resent on each later turn. Start a new chat for a new question.