A search API failover pattern routes requests to a primary API, falls back to a secondary on failure, and returns cached results if both fail. This prevents agent downtime when a single provider has an outage.
Prerequisites
- Python 3.9+
- Scavio API key
- One backup search API key (Brave or Serper)
- requests, functools
Walkthrough
Step 1: Define provider configurations
List your providers in priority order with their API call implementations.
import requests
PROVIDERS = [
{
"name": "scavio",
"fn": lambda query, n: requests.post(
"https://api.scavio.dev/api/v1/search",
json={"query": query, "num_results": n},
headers={"x-api-key": "your-scavio-api-key"},
timeout=10
).json().get("organic_results", [])
},
{
"name": "brave",
"fn": lambda query, n: requests.get(
"https://api.search.brave.com/res/v1/web/search",
params={"q": query, "count": n},
headers={"X-Subscription-Token": "your-brave-key"},
timeout=10
).json().get("web", {}).get("results", [])
}
]Step 2: Build the failover search function
Try providers in order. Catch exceptions and move to the next provider.
import time
from functools import lru_cache
_CACHE: dict = {}
def search_with_failover(query: str, n: int = 5) -> list:
for provider in PROVIDERS:
try:
results = provider["fn"](query, n)
if results:
_CACHE[query] = results # Update cache on success
print(f"[search] Used: {provider['name']}")
return results
except Exception as e:
print(f"[search] {provider['name']} failed: {e}")
time.sleep(0.5)
# All providers failed: return cached result
if query in _CACHE:
print("[search] Returning cached results")
return _CACHE[query]
return [] # Total failureStep 3: Add exponential backoff for retries
Retry a single provider up to 2 times before moving to fallback.
def call_with_retry(fn, query: str, n: int, max_retries: int = 2) -> list:
for attempt in range(max_retries + 1):
try:
result = fn(query, n)
if result:
return result
except Exception as e:
if attempt < max_retries:
wait = 2 ** attempt # 1s, 2s
print(f"Retry {attempt + 1} in {wait}s...")
time.sleep(wait)
else:
raise e
return []
def search_resilient(query: str, n: int = 5) -> list:
for provider in PROVIDERS:
try:
results = call_with_retry(provider["fn"], query, n)
if results:
_CACHE[query] = results
return results
except Exception as e:
print(f"{provider['name']} exhausted after retries: {e}")
return _CACHE.get(query, [])Python Example
import requests
import time
from datetime import datetime
SCAVIO_KEY = "your-scavio-api-key"
BRAVE_KEY = "your-brave-key" # Optional backup
PROVIDERS = [
{"name": "scavio", "fn": lambda q, n: requests.post(
"https://api.scavio.dev/api/v1/search",
json={"query": q, "num_results": n},
headers={"x-api-key": SCAVIO_KEY}, timeout=10
).json().get("organic_results", [])},
{"name": "brave", "fn": lambda q, n: requests.get(
"https://api.search.brave.com/res/v1/web/search",
params={"q": q, "count": n},
headers={"X-Subscription-Token": BRAVE_KEY}, timeout=10
).json().get("web", {}).get("results", [])}
]
_CACHE: dict = {}
def call_with_retry(fn, query, n, retries=2):
for i in range(retries + 1):
try:
r = fn(query, n)
if r: return r
except Exception as e:
if i < retries:
time.sleep(2 ** i)
else:
raise
return []
def search(query: str, n: int = 5) -> dict:
for provider in PROVIDERS:
try:
results = call_with_retry(provider["fn"], query, n)
if results:
_CACHE[query] = results
return {"results": results, "provider": provider["name"], "cached": False}
except Exception as e:
print(f"[{provider['name']}] failed: {e}")
if query in _CACHE:
return {"results": _CACHE[query], "provider": "cache", "cached": True}
return {"results": [], "provider": "none", "cached": False}
if __name__ == "__main__":
for q in ["best AI coding tools 2026", "vector database pricing"]:
result = search(q)
print(f"Q: {q}")
print(f" Provider: {result['provider']} | Results: {len(result['results'])} | Cached: {result['cached']}")JavaScript Example
const SCAVIO_KEY = 'your-scavio-api-key';
const BRAVE_KEY = 'your-brave-key';
const cache = new Map();
const PROVIDERS = [
{
name: 'scavio',
fn: async (query, n) => {
const res = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', signal: AbortSignal.timeout(10000),
headers: { 'Content-Type': 'application/json', 'x-api-key': SCAVIO_KEY },
body: JSON.stringify({ query, num_results: n })
});
return (await res.json()).organic_results ?? [];
}
}
];
async function search(query, n = 5) {
for (const provider of PROVIDERS) {
try {
const results = await provider.fn(query, n);
if (results.length) { cache.set(query, results); return { results, provider: provider.name }; }
} catch (e) {
console.log(`${provider.name} failed: ${e.message}`);
}
}
if (cache.has(query)) return { results: cache.get(query), provider: 'cache' };
return { results: [], provider: 'none' };
}
const r = await search('best AI coding tools 2026');
console.log(`Provider: ${r.provider} | Results: ${r.results.length}`);Expected Output
[scavio] Used primary
Q: best AI coding tools 2026
Provider: scavio | Results: 5 | Cached: False
# If Scavio is down:
[scavio] failed: Connection timeout
[brave] Used fallback
Q: best AI coding tools 2026
Provider: brave | Results: 5 | Cached: False
# If both down:
[scavio] failed: Connection timeout
[brave] failed: API error 503
Q: best AI coding tools 2026
Provider: cache | Results: 5 | Cached: True