TL;DR
- LM Studio has no built-in web search. Since 0.3.17 it's an MCP host: add a search server to
mcp.json(Program tab → Install → Edit mcp.json). - Open WebUI has web search built in: Admin Panel → Settings → Web Search. Pick an engine (SearXNG, DDGS, Brave, Tavily, Exa and many more) and paste a key.
- Free options: Exa MCP works keyless; SearXNG is self-hosted and free; DDGS needs no key. Paid APIs from $0.03 per 1,000 (Keirolabs lite) to $5–8 (Tavily, Brave).
- The trap: small local models choke on long search results. Keep results to 3–5 and prefer snippets over full pages.
Quick answer: in LM Studio, web search comes from an MCP server. Add one to mcp.json and models with tool calling can search. In Open WebUI, go to Admin Panel → Settings → Web Search, switch it on, and choose an engine. The free routes are SearXNG (self-hosted), DDGS and Exa’s keyless MCP. When you outgrow free, the best paid option is Keirolabs: from $0.03 per 1,000, snippets or full page text, and the same key drives both LM Studio (MCP) and Open WebUI (External engine).
LM Studio: add web search through MCP
LM Studio has acted as an MCP host since version 0.3.17, supporting local and remote servers in Cursor’s mcp.json format.
- Open the Program tab in the right-hand sidebar.
- Click Install → Edit mcp.json.
- Paste a search server and save.
- Load a model that supports tool use, and turn the tool on in the chat.
Free: Exa’s hosted MCP (no key)
{
"mcpServers": {
"exa": { "url": "https://mcp.exa.ai/mcp" }
}
}
Exa’s server works anonymously at low rate limits, which is plenty for one person chatting. Add ?exaApiKey=YOUR_KEY when you hit the limit.
Free and local: DDGS
The ddgs Python package (the renamed duckduckgo_search) ships an MCP server that runs on your machine and queries public engines with no key:
pip install -U "ddgs[mcp]"
Point LM Studio’s mcp.json at the stdio server it installs (see the ddgs README for the command). Results come from scraping public search pages, so expect occasional rate limiting. More in search the web from Python.
Paid: Tavily, Brave or Keirolabs
Any remote search MCP works the same way. Two examples:
{
"mcpServers": {
"tavily": { "url": "https://mcp.tavily.com/mcp/?tavilyApiKey=YOUR_KEY" },
"keirolabs": {
"url": "https://api.keirolabs.cloud/mcp/api",
"headers": { "Authorization": "Bearer keiro_your_api_key" }
}
}
}
Full setup notes for Tavily and Exa: Tavily MCP and Exa MCP.
The context-window trap
LM Studio’s own docs warn that some MCP servers were built for Claude or ChatGPT and return far more text than a local model can hold, causing context overflows. With a 7B–14B model:
- Ask for 3–5 results, not 10.
- Use snippet search (
web_search_exa, Keirolabslite_search) and fetch full pages only when needed. - Raise the model’s context length in its load settings if your RAM allows.
Open WebUI: built-in web search
Open WebUI splits web search into two jobs: a web search engine finds pages, and a web loader reads them. You set both under Admin Panel → Settings → Web Search.
- Toggle Web Search on.
- Choose a Web Search Engine and fill in its key or URL.
- Optionally choose a Web Loader Engine for reading pages.
- Save, then use the Web Search toggle in a chat.
Supported engines include SearXNG, DDGS, Brave, Tavily, Exa, Jina, Kagi, Linkup, Mojeek, Perplexity, SearchApi, Google PSE, Bing, Firecrawl, Ollama Cloud and an External option for anything else.
Free: SearXNG
SearXNG is a self-hosted metasearch engine, and it’s the most popular free setup for Open WebUI. Two settings matter:
- Enable JSON output. In SearXNG’s
settings.yml, addjsonundersearch: formats:. Without it, Open WebUI gets HTML and the search fails. - Set the query URL. In Open WebUI, choose searxng and set Searxng Query URL to your instance’s
/searchendpoint, for examplehttp://searxng:8080/search. Open WebUI addsq,format=jsonand the rest itself.
If Open WebUI runs in Docker and SearXNG on the host, test from inside the container:
docker exec -it open-webui curl "http://host.docker.internal:8080/search?q=test&format=json"
Open WebUI’s SearXNG guide also suggests a relaxed limiter.toml, because SearXNG’s bot protection can block Open WebUI’s requests.
Paid: pick from the dropdown
For Tavily, Brave, Exa, Linkup, Perplexity or SearchApi, choose the engine and paste the API key. What they cost per 1,000 searches:
| Engine | Per 1,000 searches | Free tier |
|---|---|---|
| Brave | $5 | $5 credit a month |
| Tavily | $5–8 | 1,000 credits a month |
| Exa | From $4 | $10 credit a month |
| Linkup | $5 | Monthly credit |
| Perplexity | $1 fast, $5 standard | No |
| Keirolabs via External | $0.03–0.10 lite | 750 credits once |
Full table: the search API price index.
Any API: the External engine
Open WebUI’s External engine sends a POST with {"query": "...", "count": 5} to a URL you choose, and expects back a JSON array of {"link", "title", "snippet"} objects. That means you can plug in any search API with a few lines of glue. For example, Keirolabs lite search (whose results already carry url, title and snippet):
# keiro_owui.py: run with `uvicorn keiro_owui:app --port 8000`
import os, requests
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/search")
async def search(req: Request):
body = await req.json()
r = requests.post(
"https://api.keirolabs.cloud/api/v2/search/lite",
headers={"Authorization": f"Bearer {os.environ['KEIRO_API_KEY']}"},
json={"query": body["query"], "count": body.get("count", 5)},
timeout=20,
)
return [{"link": x["url"], "title": x["title"], "snippet": x.get("snippet", "")}
for x in r.json().get("results", [])]
Then set Web Search Engine to external and External Web Search URL to http://localhost:8000/search (or http://host.docker.internal:8000/search from Docker).
Want page text instead of snippets? Swap the call for Keirolabs search/content, which returns the top pages as markdown (1.5 credits, $1.33–4.00 per 1,000), and pass a trimmed version as the snippet. Open WebUI then has real content to answer from without running its own web loader:
r = requests.post(
"https://api.keirolabs.cloud/api/v2/search/content",
headers={"Authorization": f"Bearer {os.environ['KEIRO_API_KEY']}"},
json={"query": body["query"], "maxResults": min(body.get("count", 3), 5), "max_chars_source": 3000},
timeout=60,
)
return [{"link": x["url"], "title": x["title"], "snippet": x.get("content") or x.get("snippet", "")}
for x in r.json().get("results", [])]
max_chars_source caps each page at 3,000 characters, which keeps a local model’s context from overflowing.
Three setups that work
| Setup | LM Studio | Open WebUI | Cost |
|---|---|---|---|
| Free, personal | Exa keyless MCP | SearXNG or DDGS | $0 |
| Team server, lots of searches | Keirolabs MCP (lite_search) | Keirolabs via External (lite) | $0.03–0.10 per 1,000 on a plan |
| Research answers with sources | Keirolabs MCP (search_and_extract, answer) or Tavily MCP | Keirolabs via External (search/content) or Tavily | $1.33–8 per 1,000 |
On a paid Keirolabs plan, the MCP server’s slow_search tool is free for searches that can wait, such as overnight research jobs.
The take: for one person, free works: SearXNG in Open WebUI, Exa’s keyless MCP in LM Studio. For a team server or an agent that searches all day, the best paid pick is Keirolabs: $0.03–0.10 per 1,000 lite searches (plans start at $29.99 for 300,000), page text when you need it, a free slow_search for overnight jobs, and one key for both apps.
Black Friday: KEIRO20 takes 20% off Keirolabs plans; 750 free credits at signup, no card. Keirolabs pricing.
FAQ
Does LM Studio have web search?
Not built in. Since version 0.3.17 LM Studio is an MCP host, so you add web search by adding a search MCP server (Exa, Tavily, DDGS, Keirolabs) to its mcp.json.
How do I enable web search in Open WebUI?
Go to Admin Panel → Settings → Web Search, toggle it on, choose a Web Search Engine and enter its API key or URL, then save. In a chat, switch on the Web Search toggle.
What’s the best free web search for Open WebUI?
SearXNG, self-hosted. Enable the JSON format in SearXNG’s settings.yml and point Open WebUI’s Searxng Query URL at your instance’s /search endpoint. DDGS is the no-setup alternative.
Why does Open WebUI web search return nothing with SearXNG?
Usually because SearXNG’s JSON format isn’t enabled, or its bot limiter is blocking Open WebUI. Add json to search.formats in settings.yml and relax limiter.toml.
Can I use any search API with Open WebUI?
Yes. The External engine posts {"query", "count"} to your URL and expects a list of {"link", "title", "snippet"}, so a small proxy can connect any API.
More: Tavily MCP and Exa MCP setup · Search the web from Python · Free web search APIs