Repository navigation
user web search
web_search lets the model search the web. It returns a URL, title and description for each result. The tool is off until you configure search.backend, and Steiner registers it only while sub-agent delegation is enabled (the default). With a backend configured, the research sub-agent also gets the tool; without one, that sub-agent type is not offered.
Each call takes a query and an optional limit (default 10, capped at 30). Only the three fields above enter the conversation, so pages are not injected wholesale; use fetch_url on a result URL when you need the content.
This page covers the three backends, their setup, a local SearXNG example, and checks for when search does not work. Field reference: search block.
- Sub-agent delegation enabled.
sub_agent.enableddefaults totrue; when it isfalse,web_searchis not registered at all, even with a backend configured. - Exactly one backend selected in
search.backend. - The credentials that backend needs, in config or in the environment.
The unprefixed environment variables below fill a config field only when that field is empty, so config wins over the environment. The backend and SearXNG URL have no environment override. Config validation fails when the selected backend is missing a required field. Restart Steiner after changing the config or its environment.
search.backend |
Credentials | Notes |
|---|---|---|
brave |
search.brave_api_key or BRAVE_API_KEY
|
Official API; see pricing. |
kagi |
search.kagi_api_key or KAGI_API_KEY
|
Paid API, billed separately from a Kagi subscription. |
searxng |
search.searxng_url |
Self-hosted metasearch. No API key. |
Brave offers a search API.
- Create an account and API key at api-dashboard.search.brave.com/register.
- Create a key in the dashboard and copy it.
- Read the Web Search getting started guide and the pricing page for current terms and limits.
Configure the backend and export the key:
search:
backend: brave# ~/.bashrc or your shell's equivalent, or set brave_api_key in config
export BRAVE_API_KEY=your-keySteiner sends the key as X-Subscription-Token to https://api.search.brave.com/res/v1/web/search and caps the returned count at 20.
Kagi requires an account, and API usage is billed separately from a Kagi subscription.
- Create a Kagi account.
- Open the API portal at kagi.com/api, or Settings then API. Add a payment method and copy the generated key. The Quick Start walks through the same steps.
- Check the key in the portal playground if you want to test it before configuring Steiner.
search:
backend: kagiexport KAGI_API_KEY=your-keySteiner's Kagi backend calls the legacy v0 endpoint, https://kagi.com/api/v0/search, with an Authorization: Bot <key> header. Kagi marks v0 as deprecated in favour of v1 (see Search API (Legacy)). Keys are issued from the same API portal, so no separate credential is needed, but if Kagi retires v0 the backend will need updating. See the legacy page for current pricing and usage terms.
SearXNG is a self-hosted metasearch front end: it fans a query out to other search engines and aggregates the results. It needs no API key, but its results depend on the engines it queries, and many public instances disable the JSON format that Steiner needs.
Requirements for a usable instance:
- The JSON format is enabled in
settings.ymlundersearch.formats. Without it, requests withformat=jsonreturn 403. - The instance is reachable from the machine running Steiner.
-
search.searxng_urlis the base URL without a trailing slash. Steiner appends/search, sendsformat=json,categories=generalandcount, and expects a response within 15 seconds.
This example runs a private instance on http://localhost:8888, reachable only from the local machine. It is a starting point, not a production deployment. For public or shared instances, use the upstream container installation guide, which covers the full Compose template, Valkey for the limiter, and reverse proxy and TLS setup.
Run these commands in Bash, not fish, to create the directory and generate a secret. The command refuses to overwrite an existing .env file:
mkdir -p searxng-local/searxng
cd searxng-local
(umask 077; set -C; printf 'SEARXNG_SECRET=%s\n' "$(openssl rand -hex 32)" > .env)compose.yaml:
services:
searxng:
image: docker.io/searxng/searxng:latest
ports:
- "127.0.0.1:8888:8080"
volumes:
- ./searxng:/etc/searxng
env_file: .env
environment:
SEARXNG_BASE_URL: http://localhost:8888/
restart: unless-stoppedsearxng/settings.yml:
use_default_settings: true
server:
limiter: false
search:
formats:
- html
- jsonNotes on this setup:
- The secret is read from
SEARXNG_SECRETin.env, not fromsettings.yml. Keep.envout of version control and do not share it. SearXNG refuses to start in production with its default secret. -
jsoninsearch.formatsis what allows the Search API to return JSON. The default format list contains onlyhtml. - The limiter is off because this example runs no Valkey and the port is bound to loopback. Do not expose the container beyond
127.0.0.1with these settings. - The example binds the host port 8888 to the container's port 8080. Change the host side if 8888 is taken, and keep
SEARXNG_BASE_URLandsearch.searxng_urlin step with it.
Start it and check the JSON endpoint:
docker compose up -d
curl -sS 'http://localhost:8888/search?q=steiner&format=json' | head -c 300A JSON body containing a results array shows the JSON endpoint is responding. This preview does not show the HTTP status; check the full response if it is truncated.
Configure Steiner:
search:
backend: searxng
searxng_url: http://localhost:8888Many public SearXNG instances disable JSON, and some block automated clients, so a public instance may not be usable for web_search. A 403 can mean JSON is disabled, but can also have other causes, such as access controls or blocking automated clients. Self-hosting lets you configure the instance, but queries still leave your machine and are sent to upstream search engines.
- Validate the config without printing its secrets:
steiner config > /dev/null && echo ok. This also validates the project config; see Configuration. - Check the provider directly with one of these. Replace the SearXNG URL if you changed the port.
# Brave
curl -sS -o /dev/null -w '%{http_code}\n' \
-H "X-Subscription-Token: $BRAVE_API_KEY" \
'https://api.search.brave.com/res/v1/web/search?q=steiner'
# Kagi (legacy v0)
curl -sS -o /dev/null -w '%{http_code}\n' \
-H "Authorization: Bot $KAGI_API_KEY" \
'https://kagi.com/api/v0/search?q=steiner'
# SearXNG
curl -sS -o /dev/null -w '%{http_code}\n' \
'http://localhost:8888/search?q=steiner&format=json'All three should return 200. A SearXNG 403 can have multiple causes, including disabled JSON or access controls.
- Start
steinerand ask something that needs current information, such as "Search the web for the current Go release and summarise the changes". Theweb_searchcall appears in the transcript.steiner toolsdoes not listweb_searcheven when it is available, becauseweb_searchis added per run when a backend is configured.
The tool returns backend errors prefixed with web_search:. Brave and SearXNG include at most the first 100 bytes of an error response body; Kagi reports the HTTP status without the body. These errors may be sent to the model provider as tool results. The curl commands above discard the body. To inspect it, repeat the relevant command without -o /dev/null and handle the output as potentially sensitive.
| Symptom | Likely cause | Fix |
|---|---|---|
| The model never searches, or says it has no search tool |
search.backend unset, or delegation disabled |
Set a backend and leave sub_agent.enabled on. |
search.backend "x" is not supported |
Typo | Use brave, kagi or searxng. |
search.backend is "brave" but brave_api_key is not set |
Key missing from config and environment | Export the variable or set the config field. The Kagi and SearXNG messages name their own fields. |
| Brave returns 401 | Key wrong, revoked or inactive | Create a key again and check the plan. |
| Brave returns 429 | Rate or credit limit reached | Slow down or add credits. Steiner caps each call at 20 results. |
| Kagi returns 401 or 403 | Key rejected or no API balance | Check the API portal and billing. |
| SearXNG returns 403 | JSON format not enabled, access controls or automated-client blocking | Check the instance settings and logs. If JSON is disabled, add json to search.formats and restart the instance. |
| SearXNG connection refused or times out | Container not running, port mismatch, or wrong host |
docker compose ps, docker compose logs searxng, and check search.searxng_url. |
| SearXNG returns an empty result list | Upstream engines blocking or throttling the instance | Run the same query in the instance's web UI and check its logs; try another instance. |
- Prefer environment variables or
${VAR}references in config over literal keys in a file. The environment variables section lists the recognised names. -
steiner configprints resolved values, including API keys and the SearXNG URL. Do not paste these into bug reports or logs. -
web_searchhonoursHTTP_PROXY,HTTPS_PROXYandNO_PROXY.fetch_urldeliberately does not; see Tools.
Get started
Using Steiner
- How to work with steiner
- Interactive mode
- CLI reference
- Skills
- Sub-agent delegation
- Oneshot
- Context management
- Advisor
- Security audit
Tools and integrations
Safety
Usage and cost
Reference
Internals and maintenance