The problem: why commercial search APIs are not the answer
Building an AI agent that needs to search the web? Your options are: pay Google $5 per 1,000 queries, hit Bing's API rate limits, or use DuckDuckGo's unofficial API that breaks without notice. None of these give you full control, and all of them send your queries — and your users' queries — to third-party servers.
For development teams in Morocco and the MENA region, there's an additional friction: regional blocks, censorship, and the fact that results from a US-centric engine are often irrelevant to local searches. A self-hosted metasearch engine sidesteps all of this.
What SearXNG provides out of the box
- 280+ upstream engines — Google, Bing, DuckDuckGo, Brave, Wikipedia, Reddit, GitHub, arXiv, YouTube, Twitter/X and hundreds more queried simultaneously.
- Zero tracking — no cookies, no user profiling, no search history stored. Privacy by architecture: the engine sends queries to upstream services, not you.
- JSON API — add
?format=jsonto any query and get machine-readable results. Natively supported by Open WebUI, n8n, Flowise, LangChain and CrewAI. - Redis caching — shared result cache reduces upstream load; rate-limiting prevents abuse on public instances.
- AGPL-3.0 — 33 k+ GitHub stars, rolling releases, Docker image updated weekly.
Architecture: two containers, under 512 MB RAM
SearXNG runs as two Docker containers: the Python engine itself (under 256 MB RAM) and a Redis instance (~30 MB) for caching and rate-limiting. No database, no persistent state beyond the Redis cache. With 1 GB of RAM, SearXNG leaves plenty of room for Nginx and the OS — a 2 GB VPS is comfortable for teams with multiple concurrent users or heavy AI agent traffic.
Self-hosting SearXNG on a VPS: step by step
Order a VPS
Choose a ServOrbit Cloud VPS with at least 1 GB RAM (Ubuntu 22.04). The provisioner handles Docker and Docker Compose installation automatically.
Create the docker-compose.yml file
Create a directory
/opt/searxngand place thisdocker-compose.ymlfile in it:services: redis: image: redis:7-alpine restart: unless-stopped command: redis-server --save "" --appendonly no volumes: - redis-data:/data searxng: image: searxng/searxng:latest restart: unless-stopped ports: - "127.0.0.1:8080:8080" volumes: - ./settings:/etc/searxng:rw environment: - SEARXNG_BASE_URL=https://search.yourdomain.com/ - SEARXNG_SECRET_KEY=CHANGE_THIS_32_CHAR_SECRET_MIN depends_on: - redis cap_drop: - ALL cap_add: - CHOWN - SETGID - SETUID - DAC_OVERRIDE volumes: redis-data:Generate a strong secret key with
openssl rand -hex 32and replaceCHANGE_THIS_32_CHAR_SECRET_MIN.Configure settings.yml
Create the
./settingsdirectory and copy the default configuration file:mkdir -p /opt/searxng/settings docker run --rm searxng/searxng:latest cat /etc/searxng/settings.yml \ > /opt/searxng/settings/settings.ymlOpen
settings.ymland adjust the key settings:general: instance_name: "My SearXNG" debug: false search: safe_search: 0 autocomplete: "" formats: - html - json server: limiter: true image_proxy: false http_protocol_version: "1.1" redis: url: redis://redis:6379/0The
redis:block points to the Redis container by its service name.Configure Nginx as a reverse proxy with TLS
Install Nginx and Certbot, then create
/etc/nginx/sites-available/searxng:server { listen 80; server_name search.yourdomain.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name search.yourdomain.com; ssl_certificate /etc/letsencrypt/live/search.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/search.yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }Enable the site and get the certificate:
certbot --nginx -d search.yourdomain.com && ln -s /etc/nginx/sites-available/searxng /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx.Start the containers
From
/opt/searxng, rundocker compose up -d. Check that both containers areUpwithdocker compose ps. View engine logs withdocker compose logs -f searxng.Enable JSON API and test
The JSON API is already enabled if you added
jsonto theformatslist insettings.yml. Test from the VPS:curl -s 'https://search.yourdomain.com/search?q=SearXNG+self+hosted&format=json' \ | jq '.results[0] | {title, url, content}'You should get a JSON object with
title,urlandcontent. If the API returns 403, verify thatformatscontainsjsonand restart the container.Enable the bot rate limiter
The built-in limiter (Redis-backed) blocks clients sending too many requests. Enable it via
limiter: trueinsettings.yml. To reinforce protection at the Nginx level, add inside theserverblock:limit_req_zone $binary_remote_addr zone=searxng:10m rate=10r/m; # inside location / : limit_req zone=searxng burst=5 nodelay;This limits each IP to 10 requests per minute with a burst tolerance of 5.
Connect Open WebUI or n8n
In Open WebUI: Settings → Web Search → Provider: SearXNG → URL:
https://search.yourdomain.com. In n8n: HTTP Request node → GEThttps://search.yourdomain.com/search?format=json, passing your query in theqparameter. Your AI agents now have live, unfiltered, private web search.
Advanced SearXNG configuration
Beyond the basic settings, settings.yml exposes options that turn SearXNG into a tool tailored for your use case.
Enable or disable engines: under the engines: key, each engine has a disabled: true field to turn it off. Disable slow or geo-blocked engines to speed up responses. For example, to keep only Google, Bing, Brave and Wikipedia for general searches:
engines:
- name: google
disabled: false
- name: bing
disabled: false
- name: brave
disabled: false
- name: duckduckgo
disabled: trueJSON response format for AI agents: JSON results contain results[].title, results[].url, results[].content (snippet), results[].engine and results[].score. Pass &categories=general,news to filter by category, or &language=en to target a language.
SafeSearch: the safe_search key accepts 0 (off), 1 (moderate) or 2 (strict). For AI agents indexing professional content, 0 returns the broadest results.
Languages and regions: add &language=ar-MA to get Moroccan Arabic results, or &language=fr-FR for French. The full list of language codes is available in the SearXNG GitHub source (file searx/languages.py).
Using SearXNG as a search source for AI agents
The /search?q=...&format=json endpoint is the universal entry point for all AI agent frameworks. Here is how to integrate it in the three most common environments.
n8n: create an *HTTP Request* node with method GET, URL https://search.yourdomain.com/search, with parameters q (query expression), format = json and optionally categories = general. A following *Code* node can extract $json.results.slice(0,5).map(r => r.url + ' — ' + r.content) to feed your LLM.
LangChain (Python): the LangChain community package includes a SearxSearchWrapper tool since version 0.1:
from langchain_community.utilities import SearxSearchWrapper
search = SearxSearchWrapper(
searx_host="https://search.yourdomain.com",
k=5
)
results = search.results("open source AI news", num_results=5)Open WebUI: once the URL is configured in Settings, each conversation can activate web search via the dedicated icon. Open WebUI queries SearXNG, injects snippets into the model context and automatically cites sources. No API key required.
Raw curl for shell scripts or minimal agents:
curl -s "https://search.yourdomain.com/search?q=searxng+api+json&format=json" \
| jq '[.results[:3] | .[] | {title, url, content}]'The result is a JSON array directly injectable into a prompt.
Securing and optimising your instance
An open SearXNG instance quickly attracts crawlers and scrapers. Here are the four levers to activate.
Redis rate limit: with limiter: true in settings.yml, Redis counts requests per IP over a sliding window. The default threshold is conservative; you can tune it in searxng/limiter.toml in recent versions of the Docker image.
HTTP Basic Auth: for an instance reserved for your team or AI agents, add inside the Nginx location / block:
auth_basic "Private SearXNG";
auth_basic_user_file /etc/nginx/.htpasswd;Generate the file with htpasswd -c /etc/nginx/.htpasswd user. AI agents pass credentials in the URL: https://user:[email protected]/search?....
Redis cache and TTL: Redis is configured by default without persistence (--save "") to avoid disk accumulation. To adjust the SearXNG cache TTL, modify the cache: key in settings.yml — for example cache: {ttl: 86400} to keep results for 24 hours and reduce calls to upstream engines.
User-Agent rotation: SearXNG automatically rotates User-Agents to avoid bans by upstream engines. If an engine starts returning CAPTCHAs, temporarily disable it with disabled: true in settings.yml and re-enable it after a few hours.
SearXNG vs alternatives: which self-hosted search engine to choose?
Scroll the table
| Criterion | SearXNG | Searx (original) | Whoogle | Brave Search API |
|---|---|---|---|---|
| Aggregated sources | 280+ engines | 100+ engines | Google only | Brave only |
| Native JSON API | Yes (`?format=json`) | Yes | No | Yes (key required) |
| Minimum RAM | ~300 MB (with Redis) | ~200 MB | ~128 MB | Zero (SaaS) |
| Active maintenance | Yes (2026) | Abandoned (archived) | Yes (2026) | Yes (SaaS) |
| Cost per query | Zero | Zero | Zero | $3 / 1,000 queries |
Restrict access on public instances
If your SearXNG instance is publicly reachable, add HTTP Basic Auth in Nginx or set limiter: true in settings.yml (Redis is required for the limiter). For AI-agent-only use, you can disable the HTML interface entirely and only expose the JSON endpoint — edit formats to remove html and restrict direct browser access.
Troubleshooting: 4 common errors
1. 429 Too Many Requests — upstream engine ban: Google or Bing has detected abnormal volume and temporarily banned your VPS IP. Symptom: SearXNG returns results from all engines except one, or the JSON response includes 'is_onion': false but no Google results. Immediate fix: disable the affected engine in settings.yml (disabled: true) and wait 2–6 hours. Long term: enable multiple redundant engines so a single ban doesn't empty results.
2. Error: Connection refused to Redis (127.0.0.1:6379): the SearXNG container is trying to reach Redis on 127.0.0.1 instead of the service name redis. Cause: the SEARXNG_REDIS_URL variable or the redis.url key in settings.yml points to localhost instead of redis://redis:6379/0. Fix: check settings.yml — the key must be url: redis://redis:6379/0 (Docker Compose service name, not localhost).
3. 403 Forbidden on /search?format=json: the JSON API is disabled. Symptom: the HTML interface works but requests with &format=json return 403. Fix: open settings.yml, verify that formats: contains json, save, then restart the container with docker compose restart searxng.
4. Slow instance (> 5 s per query): too many engines enabled simultaneously or geo-blocked engines timing out. Diagnosis: check logs with docker compose logs searxng | grep 'timeout\|error'. Fix: reduce active engines to the 5–8 most relevant for your use case, and increase the global timeout in settings.yml (request_timeout: 3.0 in seconds).
SearXNG as the search backbone for your AI stack
SearXNG has become the de facto web search backend for self-hosted AI stacks in 2026. Open WebUI, n8n, Flowise, LangChain, CrewAI and most LLM orchestration frameworks have native SearXNG integration. The pattern is always the same: your LLM decides it needs current information, calls your SearXNG endpoint with a query, gets back a JSON array of results with titles, URLs and snippets, and uses those to ground its response.
This eliminates three pain points in one deployment: no per-query billing, no data leakage to Google or Bing, and no rate limit surprises at 2 AM when your agent is running a research batch.