Tutorial

How to Migrate n8n from SerpAPI to Scavio

Step-by-step migration from SerpAPI to Scavio in n8n workflows. Map response fields, update HTTP nodes, and test the switch in under 15 minutes.

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.

Python
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.

Python
# 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.

Python
# 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.

Python
# 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.

Python
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

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', '')}")

JavaScript Example

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)));

Expected Output

JSON
A fully migrated n8n workflow using Scavio instead of SerpAPI, with updated HTTP nodes, response field mappings, and validation tests confirming equivalent output.

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

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). A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Step-by-step migration from SerpAPI to Scavio in n8n workflows. Map response fields, update HTTP nodes, and test the switch in under 15 minutes.