openwebuitoolsearch

Search API as OpenWebUI Native Tool

Add Scavio as a native OpenWebUI tool function. Structured JSON results, multi-platform search, and no SearXNG maintenance. Drop-in Python function.

7 min

You can configure a structured search API as an OpenWebUI native tool instead of relying on the default SearXNG web search integration. Native tool calling mode lets the LLM decide when to search and how to use results, which produces better answers than the automatic web search injection that prepends raw results to every prompt.

Why Native Tool Calling Beats Default Web Search

OpenWebUI's default web search mode (SearXNG, Brave, etc.) injects search results into the system prompt before the LLM sees it. This has problems: the LLM cannot control what gets searched, results bloat the context window on every turn, and the search query is just the raw user message rather than a refined query. Native tool calling mode lets the model decide if a search is needed, craft a targeted query, and process results selectively.

The Tool Function Schema

OpenWebUI native tools are Python functions with a specific signature. The function docstring becomes the tool description the LLM sees.

Python
import requests
from typing import Optional

class Tools:
    def __init__(self):
        self.api_key = ""  # Set in OpenWebUI tool settings

    def web_search(
        self,
        query: str,
        num_results: Optional[int] = 5,
        __user__: dict = {},
    ) -> str:
        """
        Search the web for current information. Use this when the user
        asks about recent events, current data, or anything that requires
        up-to-date information beyond your training data.

        :param query: The search query string.
        :param num_results: Number of results to return (1-10).
        :return: Search results as formatted text.
        """
        resp = requests.post(
            "https://api.scavio.dev/api/v1/search",
            headers={"x-api-key": self.api_key},
            json={"query": query, "num_results": num_results or 5},
            timeout=15,
        )
        if resp.status_code != 200:
            return f"Search failed: {resp.status_code}"
        results = resp.json().get("results", [])
        if not results:
            return "No results found."
        output = []
        for i, r in enumerate(results, 1):
            output.append(
                f"{i}. {r.get('title', 'No title')}\n"
                f"   URL: {r.get('url', '')}\n"
                f"   {r.get('snippet', 'No snippet')}"
            )
        return "\n\n".join(output)

Setting It Up in OpenWebUI

  1. Go to Workspace, then Tools, then click Create Tool
  2. Paste the function code above
  3. In the tool settings, set the api_key valves field to your Scavio API key
  4. Enable the tool in your model configuration under Tool settings
  5. In the chat, make sure native tool calling is enabled (the wrench icon, not the globe icon)

Adding a Reddit Search Tool

You can add a second tool function to the same class for Reddit-specific searches.

Python
    def reddit_search(
        self,
        query: str,
        subreddit: Optional[str] = None,
        __user__: dict = {},
    ) -> str:
        """
        Search Reddit for discussions and opinions. Use this when the user
        wants community perspectives, reviews, or recommendations.

        :param query: The search query.
        :param subreddit: Optional subreddit to search within.
        :return: Reddit discussion results.
        """
        search_query = query
        if subreddit:
            search_query = f"site:reddit.com/r/{subreddit} {query}"
        else:
            search_query = f"site:reddit.com {query}"
        resp = requests.post(
            "https://api.scavio.dev/api/v1/search",
            headers={"x-api-key": self.api_key},
            json={"query": search_query, "num_results": 8},
            timeout=15,
        )
        results = resp.json().get("results", [])
        output = []
        for i, r in enumerate(results, 1):
            output.append(
                f"{i}. {r.get('title', '')}\n"
                f"   {r.get('url', '')}\n"
                f"   {r.get('snippet', '')}"
            )
        return "\n\n".join(output) if output else "No Reddit results found."

SearXNG vs Structured API Comparison

  • SearXNG: free, self-hosted, but returns HTML-scraped results that break when upstream engines change their markup
  • SearXNG requires Docker setup and ongoing maintenance for rate limit bans from Google/Bing
  • Structured search API: $0.005/query, no infrastructure, stable JSON schema, no scraping risk
  • At 50 searches/day: SearXNG = $0 + server cost + your time. Scavio = $7.50/mo. The question is whether your time maintaining SearXNG is worth $7.50.

Valves Configuration

OpenWebUI valves let users configure tool settings without editing code. Add a Pydantic model to expose the API key as a configurable field.

Python
from pydantic import BaseModel, Field

class Tools:
    class Valves(BaseModel):
        api_key: str = Field(default="", description="Scavio API key")
        default_results: int = Field(default=5, description="Default result count")

    def __init__(self):
        self.valves = self.Valves()

    def web_search(self, query: str, __user__: dict = {}) -> str:
        """Search the web for current information."""
        resp = requests.post(
            "https://api.scavio.dev/api/v1/search",
            headers={"x-api-key": self.valves.api_key},
            json={"query": query, "num_results": self.valves.default_results},
            timeout=15,
        )
        # ... same result formatting as above

When to Use Which Mode

Use native tool calling when you want the LLM to decide when searching is relevant. Use the default web search mode when every single user message should be grounded in fresh web data (like a customer support bot answering about current policies). For most general-purpose chat setups, native tool calling gives better results because it avoids polluting the context with irrelevant search results on conversational turns.