Two recent Reddit posts shipped self-hosted SearXNG variants as free SerpAPI alternatives. This walks the deployment plus the operational reality that decides when to pay for hosted instead.
Prerequisites
- A small VPS or Railway/Render account
- Docker basics
- Awareness that you own captcha/Cloudflare handling
Walkthrough
Step 1: Deploy SearXNG via Docker
Official image, settings.yml with JSON output enabled.
docker run -d --name searxng -p 8888:8080 \
-v $(pwd)/searxng:/etc/searxng \
-e BASE_URL=http://localhost:8888 \
-e INSTANCE_NAME=my-searxng \
searxng/searxng:latestStep 2: Enable JSON output in settings.yml
By default, JSON is off on public instances.
# /etc/searxng/settings.yml
search:
formats:
- html
- jsonStep 3: Hit /search with format=json
Standard SearXNG API shape.
curl 'http://localhost:8888/search?q=ai+agents+2026&format=json' | jq .results[0]Step 4: Add a Redis rate-limit layer
SearXNG rate-limits per IP via Redis.
docker run -d --name searxng-redis -p 6379:6379 redis:alpine
# Update settings.yml: redis.url: redis://searxng-redis:6379Step 5: Decide: keep self-hosted or pay
Decision rule based on volume and uptime.
# Stay self-hosted when:
# - <500 queries/day and intermittent failure is OK
# - Privacy / no-vendor is a hard requirement
# - You enjoy operating things
# Pay $30/mo Scavio when:
# - Production agents on cron need stable uptime
# - You hit Cloudflare/captcha walls weekly
# - Ops time costs more than $30/moPython Example
import requests
result = requests.get('http://localhost:8888/search', params={'q': 'ai agents', 'format': 'json'}).json()JavaScript Example
const result = await fetch('http://localhost:8888/search?q=ai+agents&format=json').then(r => r.json());Expected Output
Self-hosted SearXNG returning JSON. Works for personal-research load. Production agents on cron will eventually hit captcha/IP issues; that is when paid hosted alternatives earn their fee.