Tutorial

How to Add a SearXNG Fallback to Your Search Pipeline

Add a free SearXNG fallback to your search pipeline with automatic failover to Scavio when structured data is needed. Python code included.

SearXNG gives you free self-hosted search results but returns unstructured HTML without consistent JSON. A hybrid approach uses SearXNG for low-priority queries and fails over to Scavio at $0.005/request when you need structured data, SERP features, or guaranteed uptime. This tutorial builds a fallback pipeline that tries SearXNG first and promotes to Scavio when SearXNG returns incomplete results.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • SearXNG instance (local or remote) with JSON format enabled

Walkthrough

Step 1: Configure SearXNG and Scavio clients

Set up both search backends with a unified interface.

Python
import os, requests, time

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
SCAVIO_H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
SEARXNG_URL = os.environ.get('SEARXNG_URL', 'http://localhost:8080')

def search_searxng(query):
    try:
        r = requests.get(f'{SEARXNG_URL}/search',
            params={'q': query, 'format': 'json', 'engines': 'google'},
            timeout=5)
        r.raise_for_status()
        data = r.json()
        return [{'title': r['title'], 'link': r['url'], 'snippet': r.get('content', '')}
                for r in data.get('results', [])[:10]]
    except Exception as e:
        print(f'SearXNG failed: {e}')
        return None

def search_scavio(query):
    r = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SCAVIO_H, json={'query': query, 'country_code': 'us'})
    return r.json()

print('Clients configured.')

Step 2: Build the fallback router

Try SearXNG first. If results are missing or incomplete, promote to Scavio.

Python
def search_with_fallback(query, need_structured=False, min_results=5):
    cost = 0.0
    source = 'searxng'
    if not need_structured:
        results = search_searxng(query)
        if results and len(results) >= min_results:
            return {'results': results, 'source': source, 'cost': cost}
        print(f'SearXNG returned {len(results) if results else 0} results, falling back to Scavio')

    source = 'scavio'
    cost = 0.005
    data = search_scavio(query)
    return {
        'results': data.get('organic_results', []),
        'source': source,
        'cost': cost,
        'serp_features': {
            'ai_overview': bool(data.get('ai_overview')),
            'answer_box': bool(data.get('answer_box')),
            'paa': len(data.get('related_questions', []))
        }
    }

r = search_with_fallback('best python web framework 2026')
print(f'Source: {r["source"]}, Results: {len(r["results"])}, Cost: ${r["cost"]:.3f}')

Step 3: Add health checks and metrics

Track SearXNG availability and fallback rate to estimate costs.

Python
from collections import Counter

stats = Counter()

def tracked_search(query, **kwargs):
    r = search_with_fallback(query, **kwargs)
    stats[r['source']] += 1
    stats['total_cost'] += r['cost']
    return r

queries = ['python web framework', 'best serp api', 'tiktok analytics tool',
           'react vs vue 2026', 'llm agent search']
for q in queries:
    r = tracked_search(q)
    print(f'  {q[:35]:35} -> {r["source"]:8} ({len(r["results"])} results)')

print(f'\nSearXNG hits: {stats["searxng"]}, Scavio fallbacks: {stats["scavio"]}')
print(f'Total cost: ${stats["total_cost"]:.3f}')
print(f'Savings vs all-Scavio: ${len(queries) * 0.005 - stats["total_cost"]:.3f}')

Step 4: Force Scavio for structured queries

Some queries always need structured SERP data. Route those directly.

Python
STRUCTURED_PATTERNS = ['price', 'cost', 'pricing', 'buy', 'vs ', 'compare']

def smart_search(query):
    need_structured = any(p in query.lower() for p in STRUCTURED_PATTERNS)
    r = tracked_search(query, need_structured=need_structured)
    if need_structured:
        print(f'  [STRUCTURED] {query[:40]} -> Scavio (SERP features: {r.get("serp_features", {})})')
    else:
        print(f'  [BASIC] {query[:40]} -> {r["source"]}')
    return r

smart_search('python tutorial')
smart_search('scavio vs serpapi pricing')
smart_search('buy noise canceling headphones')
print(f'Total cost: ${stats["total_cost"]:.3f}')

Python Example

Python
import os, requests

SCAVIO_H = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
SEARXNG = os.environ.get('SEARXNG_URL', 'http://localhost:8080')

def search(query, force_scavio=False):
    if not force_scavio:
        try:
            r = requests.get(f'{SEARXNG}/search', params={'q': query, 'format': 'json'}, timeout=5)
            results = r.json().get('results', [])[:10]
            if len(results) >= 5:
                print(f'{query}: {len(results)} results via SearXNG (free)')
                return results
        except: pass
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SCAVIO_H, json={'query': query, 'country_code': 'us'}).json()
    print(f'{query}: {len(data.get("organic_results", []))} results via Scavio ($0.005)')
    return data.get('organic_results', [])

search('python api tutorial')
search('serp api pricing comparison', force_scavio=True)

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
const SEARXNG = process.env.SEARXNG_URL || 'http://localhost:8080';

async function search(query, forceScavio = false) {
  if (!forceScavio) {
    try {
      const r = await fetch(`${SEARXNG}/search?q=${encodeURIComponent(query)}&format=json`,
        { signal: AbortSignal.timeout(5000) });
      const data = await r.json();
      if (data.results?.length >= 5) {
        console.log(`${query}: ${data.results.length} via SearXNG (free)`);
        return data.results.slice(0, 10);
      }
    } catch {}
  }
  const data = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST', headers: SH,
    body: JSON.stringify({ query, country_code: 'us' })
  }).then(r => r.json());
  console.log(`${query}: ${(data.organic_results||[]).length} via Scavio ($0.005)`);
  return data.organic_results || [];
}
search('python tutorial').then(() => search('serp api pricing', true)).catch(console.error);

Expected Output

JSON
  python web framework               -> searxng  (8 results)
  best serp api                       -> scavio   (10 results)
  tiktok analytics tool               -> searxng  (7 results)
  react vs vue 2026                   -> searxng  (9 results)
  llm agent search                    -> scavio   (10 results)

SearXNG hits: 3, Scavio fallbacks: 2
Total cost: $0.010
Savings vs all-Scavio: $0.015

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.8+. requests library. A Scavio API key from scavio.dev. SearXNG instance (local or remote) with JSON format enabled. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Add a free SearXNG fallback to your search pipeline with automatic failover to Scavio when structured data is needed. Python code included.