Migre sus flujos de trabajo n8n de SerpAPI a Scavio actualizando la URL del nodo de solicitud HTTP, cambiando el encabezado de la clave API y ajustando las rutas del campo de respuesta. La migración tarda menos de 15 minutos porque ambas API devuelven datos de búsqueda estructurados similares. Scavio utiliza un punto final POST con un cuerpo JSON en lugar del GET de SerpAPI con parámetros de consulta, y los nombres de los campos de respuesta difieren ligeramente. Este tutorial mapea cada cambio de campo y proporciona código antes/después de cada paso.
Requisitos previos
- Instancia n8n en ejecución (autohospedada o nube n8n)
- Una clave API de Scavio de scavio.dev
- Flujos de trabajo n8n existentes que utilizan SerpAPI
- Clave API SerpAPI (para pruebas de comparación)
Guia paso a paso
Paso 1: Mapear el formato de solicitud
Convierta de la solicitud GET de SerpAPI al formato de solicitud POST de Scavio.
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', []))}")Paso 2: Asignar campos de respuesta
Asigne nombres de campos de respuesta de SerpAPI a nombres de campos de Scavio para cada tipo de datos.
# 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', []))}")Paso 3: Actualizar el nodo de solicitud HTTP n8n
Configure el nodo de solicitud HTTP n8n para la API de Scavio.
# 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]}")Paso 4: Actualizar nodos posteriores
Ajuste cualquier expresión n8n que haga referencia a rutas de campo específicas de SerpAPI.
# 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}')Paso 5: Probar la migración
Ejecute pruebas de validación para confirmar que la migración produce resultados equivalentes.
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.')Ejemplo en Python
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', '')}")Ejemplo en JavaScript
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)));Salida esperada
A fully migrated n8n workflow using Scavio instead of SerpAPI, with updated HTTP nodes, response field mappings, and validation tests confirming equivalent output.