Migrate your n8n workflows from SerpAPI to Scavio by updating the HTTP Request node URL, switching the API key header, and adjusting the response field paths. The migration takes under 15 minutes because both APIs return similar structured search data. Scavio uses a POST endpoint with a JSON body instead of SerpAPI's GET with query parameters, and the response field names differ slightly. This tutorial maps every field change and provides before/after code for each step.
Prerequisites
- n8n instance running (self-hosted or n8n Cloud)
- A Scavio API key from scavio.dev
- Existing n8n workflows using SerpAPI
- SerpAPI API key (for comparison testing)
Walkthrough
Step 1: Map the request format
Convert from SerpAPI GET request to Scavio POST request format.
import os, requests
# BEFORE (SerpAPI):
# resp = requests.get('https://serpapi.com/search',
# params={'api_key': os.environ['SERPAPI_KEY'], 'q': 'test', 'engine': 'google'})
# AFTER (Scavio):
API_KEY = os.environ['SCAVIO_API_KEY']
def search(query: str, platform: str = 'google') -> dict:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': platform, 'query': query}, timeout=15)
return resp.json()
# Key differences:
# SerpAPI: GET, api_key in params, engine=google
# Scavio: POST, x-api-key in header, platform=google in body
result = search('test query')
print(f"Results: {len(result.get('organic_results', []))}")Step 2: Map response fields
Map SerpAPI response field names to Scavio field names for each data type.
# Field mapping: SerpAPI -> Scavio
FIELD_MAP = {
# Organic results
'organic_results': 'organic_results', # Same!
'title': 'title', # Same!
'link': 'link', # Same!
'snippet': 'snippet', # Same!
'position': 'position', # Same!
# Other fields
'search_information': 'search_information',
'related_questions': 'people_also_ask', # Different!
'answer_box': 'featured_snippet', # Different!
}
def serpapi_to_scavio(serpapi_result: dict) -> dict:
"""Convert SerpAPI response format to match expected fields."""
# Most organic result fields are identical
# Main differences are in the wrapper fields
return {
'organic_results': serpapi_result.get('organic_results', []),
'people_also_ask': serpapi_result.get('related_questions', []),
'featured_snippet': serpapi_result.get('answer_box', {}),
}
# In practice, Scavio returns these fields directly:
data = search('best CRM 2026')
print(f"Organic: {len(data.get('organic_results', []))}")
print(f"PAA: {len(data.get('people_also_ask', []))}")Step 3: Update n8n HTTP Request node
Configure the n8n HTTP Request node for the Scavio API.
# n8n HTTP Request Node Configuration:
#
# BEFORE (SerpAPI):
# Method: GET
# URL: https://serpapi.com/search
# Query Parameters:
# api_key: {{$env.SERPAPI_KEY}}
# q: {{$json.query}}
# engine: google
#
# AFTER (Scavio):
# Method: POST
# URL: https://api.scavio.dev/api/v1/search
# Headers:
# x-api-key: {{$env.SCAVIO_API_KEY}}
# Body Content Type: JSON
# Body: {"platform": "google", "query": "{{$json.query}}"}
# Test both side by side:
def compare_responses(query: str) -> dict:
scavio = search(query)
scavio_results = scavio.get('organic_results', [])
return {
'query': query,
'scavio_count': len(scavio_results),
'scavio_top': scavio_results[0].get('title', '') if scavio_results else 'none',
}
comp = compare_responses('best CRM 2026')
print(f"Scavio: {comp['scavio_count']} results, top: {comp['scavio_top'][:50]}")Step 4: Update downstream nodes
Adjust any n8n expressions that reference SerpAPI-specific field paths.
# n8n Expression Updates:
#
# BEFORE (SerpAPI response paths):
# {{$json.organic_results[0].title}} -> Same
# {{$json.organic_results[0].link}} -> Same
# {{$json.organic_results[0].snippet}} -> Same
# {{$json.related_questions}} -> {{$json.people_also_ask}}
# {{$json.answer_box.snippet}} -> {{$json.featured_snippet.snippet}}
# {{$json.search_information.total_results}} -> {{$json.search_information.total_results}}
# Python equivalent for testing:
def extract_n8n_fields(data: dict) -> dict:
results = data.get('organic_results', [])
return {
'top_title': results[0].get('title', '') if results else '',
'top_link': results[0].get('link', '') if results else '',
'top_snippet': results[0].get('snippet', '') if results else '',
'paa': data.get('people_also_ask', []),
'featured': data.get('featured_snippet', {}),
'result_count': len(results),
}
data = search('best CRM software')
fields = extract_n8n_fields(data)
for key, value in fields.items():
display = str(value)[:60] if value else 'empty'
print(f' {key}: {display}')Step 5: Test the migration
Run validation tests to confirm the migration produces equivalent results.
def validate_migration(queries: list) -> dict:
passed = 0
failed = 0
for query in queries:
data = search(query)
results = data.get('organic_results', [])
# Check essential fields exist
checks = {
'has_results': len(results) > 0,
'has_titles': all(r.get('title') for r in results[:3]) if results else False,
'has_links': all(r.get('link') for r in results[:3]) if results else False,
'valid_json': isinstance(data, dict),
}
all_pass = all(checks.values())
if all_pass:
passed += 1
else:
failed += 1
print(f'FAIL: {query}: {checks}')
print(f'\nMigration validation: {passed} passed, {failed} failed')
return {'passed': passed, 'failed': failed}
validate_migration([
'best CRM 2026',
'python web framework comparison',
'how to deploy docker',
])
# Cost comparison:
# SerpAPI: $25/mo (5K searches), $75/mo (15K)
# Scavio: Free 250/mo, $30/mo (7K credits)
print('\nMigration complete. Cost savings start immediately.')Python Example
import requests, os
H = {'x-api-key': os.environ['SCAVIO_API_KEY']}
# SerpAPI drop-in replacement:
def search(query, platform='google'):
data = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'platform': platform, 'query': query}).json()
return data.get('organic_results', [])
results = search('best CRM 2026')
for r in results[:3]:
print(f"{r.get('title', '')}: {r.get('link', '')}")JavaScript Example
const H = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};
// SerpAPI drop-in replacement:
async function search(query, platform = 'google') {
const r = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: H,
body: JSON.stringify({platform, query})
});
return (await r.json()).organic_results || [];
}
search('best CRM 2026').then(results => results.slice(0, 3).forEach(r => console.log(r.title)));Expected Output
A fully migrated n8n workflow using Scavio instead of SerpAPI, with updated HTTP nodes, response field mappings, and validation tests confirming equivalent output.