Migrez vos workflows n8n de SerpAPI vers Scavio en mettant à jour l'URL du nœud HTTP Request, en changeant l'en-tête de la clé API et en ajustant les chemins des champs de réponse. La migration prend moins de 15 minutes car les deux API renvoient des données de recherche structurées similaires. Scavio utilise un endpoint POST avec un corps JSON au lieu de la méthode GET de SerpAPI avec des paramètres de requête, et les noms des champs de réponse diffèrent légèrement. Ce tutoriel mappe chaque changement de champ et fournit le code avant/après pour chaque étape.
Prérequis
- Instance n8n en cours d'exécution (auto-hébergée ou n8n Cloud)
- Une clé API Scavio depuis scavio.dev
- Workflows n8n existants utilisant SerpAPI
- Clé API SerpAPI (pour les tests de comparaison)
Parcours
Étape 1: Mapper le format de la requête
Convertir du format de requête GET de SerpAPI au format de requête 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', []))}")Étape 2: Mapper les champs de réponse
Mapper les noms des champs de réponse de SerpAPI vers Scavio pour chaque type de données.
# 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', []))}")Étape 3: Mettre à jour le nœud HTTP Request de n8n
Configurer le nœud HTTP Request de n8n pour l'API 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]}")Étape 4: Mettre à jour les nœuds en aval
Ajuster toutes les expressions n8n qui référencent des chemins de champs spécifiques à 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}')Étape 5: Tester la migration
Exécuter des tests de validation pour confirmer que la migration produit des résultats équivalents.
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.')Exemple 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', '')}")Exemple 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)));Sortie attendue
A fully migrated n8n workflow using Scavio instead of SerpAPI, with updated HTTP nodes, response field mappings, and validation tests confirming equivalent output.