Hermes v0.13.0 shipped with a broken web search tool that returns empty results or times out for many queries. The issue stems from its default search backend being rate-limited and unreliable. Rather than waiting for a patch, you can swap in a working search API backend in minutes. This tutorial shows how to replace the Hermes search tool with the Scavio API, which returns structured SERP data reliably at $0.005 per query. The fix requires changing only the search function -- no other agent code changes needed.
Prerequisites
- Hermes v0.13.x installed
- Python 3.9+ installed
- requests library installed
- A Scavio API key from scavio.dev
Walkthrough
Step 1: Diagnose the broken search tool
First, confirm the search issue by running a test query through Hermes and checking the tool output. The typical failure modes are empty results, timeout errors, or malformed JSON.
# Test the current Hermes search tool:
# In your Hermes agent, run a query that requires search:
# "What is the current price of Bitcoin?"
#
# Common error outputs:
# - {"results": []} (empty results)
# - TimeoutError: Search request timed out after 30s
# - JSONDecodeError: Expecting value (malformed response)
#
# If you see any of these, the search backend is broken.
import requests
# Quick test of the default Hermes search endpoint:
try:
resp = requests.get('http://localhost:8080/search?q=test', timeout=10)
print(f'Status: {resp.status_code}')
print(f'Results: {len(resp.json().get("results", []))}')
except Exception as e:
print(f'Search broken: {e}')Step 2: Build the replacement search function
Create a drop-in replacement that matches the Hermes search tool interface. The function takes a query string and returns results in the format Hermes expects.
import requests, os
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def hermes_search_replacement(query: str, num_results: int = 5) -> dict:
"""Drop-in replacement for broken Hermes search tool.
Returns results in Hermes-compatible format."""
try:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us'},
timeout=15)
resp.raise_for_status()
data = resp.json()
results = [{
'title': r['title'],
'url': r['link'],
'content': r.get('snippet', ''),
} for r in data.get('organic_results', [])[:num_results]]
return {'results': results}
except Exception as e:
return {'results': [], 'error': str(e)}
# Test the replacement:
result = hermes_search_replacement('current bitcoin price')
print(f'Results: {len(result["results"])}')
for r in result['results']:
print(f' {r["title"]}')Step 3: Patch the Hermes search tool registration
Override the default search tool in your Hermes agent configuration. This replaces the broken backend without modifying Hermes source code.
# Option 1: Override in your agent setup script
# Find where Hermes registers its search tool and replace it:
def register_fixed_search(agent):
"""Replace the broken Hermes search with working API."""
# Remove the broken tool
if hasattr(agent, 'tools'):
agent.tools = {name: tool for name, tool in agent.tools.items()
if name != 'web_search'}
# Register the working replacement
agent.register_tool(
name='web_search',
description='Search the web for current information.',
function=hermes_search_replacement,
parameters={
'query': {'type': 'string', 'description': 'Search query'},
'num_results': {'type': 'integer', 'description': 'Number of results', 'default': 5}
}
)
print('Search tool replaced successfully')
# Option 2: Environment variable override
# Some Hermes configs support:
# HERMES_SEARCH_BACKEND=custom
# HERMES_SEARCH_ENDPOINT=https://api.scavio.dev/api/v1/searchStep 4: Verify the fix works end to end
Run test queries through the patched Hermes agent to confirm search works correctly. Test edge cases like empty results and long queries.
def verify_search_fix():
test_cases = [
('current bitcoin price', True),
('best python frameworks 2026', True),
('xyznonexistentquery12345', False), # should return empty gracefully
('a' * 500, False), # very long query
]
passed = 0
for query, expect_results in test_cases:
result = hermes_search_replacement(query)
has_results = len(result.get('results', [])) > 0
has_error = 'error' in result
if expect_results and has_results:
status = 'PASS'
passed += 1
elif not expect_results and not has_error:
status = 'PASS'
passed += 1
else:
status = 'FAIL'
print(f'[{status}] "{query[:40]}" -> {len(result.get("results", []))} results')
print(f'\n{passed}/{len(test_cases)} tests passed')
print(f'Cost: ${len(test_cases) * 0.005:.3f}')
verify_search_fix()Python Example
import os, requests
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def hermes_search(query: str, num_results: int = 5) -> dict:
try:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us'}, timeout=15)
results = [{'title': r['title'], 'url': r['link'], 'content': r.get('snippet', '')}
for r in resp.json().get('organic_results', [])[:num_results]]
return {'results': results}
except Exception as e:
return {'results': [], 'error': str(e)}
# Quick test
for q in ['bitcoin price', 'python tutorial', 'nonexistent12345']:
r = hermes_search(q)
print(f'{q}: {len(r["results"])} results')JavaScript Example
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function hermesSearch(query, numResults = 5) {
try {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us' })
});
const data = await resp.json();
return { results: (data.organic_results || []).slice(0, numResults)
.map(r => ({ title: r.title, url: r.link, content: r.snippet || '' })) };
} catch (e) {
return { results: [], error: e.message };
}
}
async function main() {
for (const q of ['bitcoin price', 'python tutorial']) {
const r = await hermesSearch(q);
console.log(`${q}: ${r.results.length} results`);
}
}
main();Expected Output
Search broken: TimeoutError: Search request timed out after 10s
Results: 5
Bitcoin Price Today - Live BTC Chart
BTC/USD - Bitcoin Price & Market Cap
[PASS] "current bitcoin price" -> 5 results
[PASS] "best python frameworks 2026" -> 5 results
[PASS] "xyznonexistentquery12345" -> 0 results
[PASS] "aaaaaaaaaa..." -> 0 results
4/4 tests passed
Cost: $0.020