Tutorial

How to Migrate from Google CSE to a Search API in 2026

Step-by-step migration from Google Custom Search Engine to a modern search API. Drop-in replacement functions, cost comparison, and testing guide.

Google Custom Search Engine (CSE) has been the default programmatic search option for years, but its limitations are well known: 100 queries/day free tier, $5/1000 queries, restricted to 10 results per page, and no SERP features like People Also Ask or AI Overviews. With Google announcing changes to CSE in 2026, teams need a migration plan. The Scavio API provides a drop-in replacement with richer data at comparable or lower cost. This tutorial walks through the complete migration.

Prerequisites

  • Python 3.9+ installed
  • Existing Google CSE integration to migrate
  • A Scavio API key from scavio.dev
  • requests library installed

Walkthrough

Step 1: Map your CSE API calls to the new format

Google CSE uses GET requests with query parameters. The replacement uses POST with a JSON body. Map each parameter to its equivalent.

Python
# Google CSE format:
# GET https://www.googleapis.com/customsearch/v1
#   ?key=YOUR_CSE_KEY
#   &cx=YOUR_CX_ID
#   &q=search+query
#   &num=10
#   &start=1
#   &gl=us

# Equivalent Scavio format:
# POST https://api.scavio.dev/api/v1/search
# Headers: x-api-key: YOUR_SCAVIO_KEY
# Body: {"query": "search query", "country_code": "us"}

param_mapping = {
    'q': 'query',           # search query
    'gl': 'country_code',   # geolocation
    'num': 'N/A',           # returns all results by default
    'start': 'N/A',         # no pagination needed
    'cx': 'N/A',            # no custom engine ID needed
    'key': 'x-api-key',    # header instead of param
}

for cse_param, scavio_param in param_mapping.items():
    print(f'CSE: {cse_param} -> Scavio: {scavio_param}')

Step 2: Build the drop-in replacement function

Create a function that matches your existing CSE call signature. Your application code only needs to swap the import.

Python
import requests, os

API_KEY = os.environ['SCAVIO_API_KEY']

def google_search(query: str, num: int = 10, gl: str = 'us', **kwargs) -> dict:
    """Drop-in replacement for Google CSE search.
    Same parameters, same return format."""
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': gl})
    resp.raise_for_status()
    data = resp.json()
    # Map to CSE-compatible format
    items = [{
        'title': r['title'],
        'link': r['link'],
        'snippet': r.get('snippet', ''),
        'displayLink': r.get('link', '').split('/')[2] if '/' in r.get('link', '') else '',
    } for r in data.get('organic_results', [])[:num]]
    return {
        'items': items,
        'searchInformation': {
            'totalResults': str(len(items)),
            'searchTime': 0.5
        }
    }

Step 3: Handle CSE-specific features

Some CSE features like site-restricted search and image search map to query modifiers. Handle these transparently in the replacement.

Python
def google_search_advanced(query: str, site: str = None,
                          search_type: str = None, **kwargs) -> dict:
    # Site-restricted search
    if site:
        query = f'site:{site} {query}'
    # Image search
    if search_type == 'image':
        resp = requests.post('https://api.scavio.dev/api/v1/search',
            headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
            json={'query': query, 'country_code': kwargs.get('gl', 'us'),
                  'type': 'images'})
        data = resp.json()
        return {'items': [{'link': r.get('link', ''), 'title': r.get('title', ''),
                           'image': r.get('image', {})}
                          for r in data.get('images_results', [])]}
    return google_search(query, **kwargs)

# Test site-restricted search:
results = google_search_advanced('pricing', site='stripe.com')
print(f'Found {len(results["items"])} results from stripe.com')

Step 4: Compare costs and feature coverage

Calculate the cost difference between CSE and the replacement API for your usage pattern.

Python
def migration_cost_comparison(monthly_queries: int) -> dict:
    # Google CSE pricing
    cse_free = 100  # per day, roughly 3000/month
    cse_paid_rate = 5.0 / 1000  # $5 per 1000 queries
    if monthly_queries <= 3000:
        cse_cost = 0
    else:
        cse_cost = (monthly_queries - 3000) * cse_paid_rate
    # Scavio pricing
    scavio_rate = 0.005
    if monthly_queries <= 250:
        scavio_cost = 0  # free tier
    else:
        scavio_cost = monthly_queries * scavio_rate
    # Feature comparison
    features = {
        'Google CSE': ['organic results', '10 per page', 'basic metadata'],
        'Scavio': ['organic results', 'all results', 'People Also Ask',
                   'AI Overviews', 'knowledge graph', 'shopping', 'images',
                   'Amazon', 'YouTube', 'TikTok']
    }
    return {
        'monthly_queries': monthly_queries,
        'cse_cost': f'${cse_cost:.2f}',
        'scavio_cost': f'${scavio_cost:.2f}',
        'savings': f'${cse_cost - scavio_cost:.2f}',
        'feature_gain': len(features['Scavio']) - len(features['Google CSE'])
    }

for vol in [1000, 5000, 20000]:
    c = migration_cost_comparison(vol)
    print(f'{c["monthly_queries"]:,} queries: CSE {c["cse_cost"]} vs Scavio {c["scavio_cost"]}')

Step 5: Test the migration with side-by-side comparison

Run the same queries through both APIs and compare results to validate the migration before switching over.

Python
def validate_migration(test_queries: list) -> None:
    print('Migration validation:')
    for query in test_queries:
        # New API
        new_results = google_search(query)
        new_count = len(new_results.get('items', []))
        top_new = new_results['items'][0]['title'] if new_results['items'] else 'N/A'
        print(f'  "{query}":')
        print(f'    New API: {new_count} results, top: {top_new[:50]}')
        print(f'    Cost: $0.005')
    total_cost = len(test_queries) * 0.005
    print(f'\nValidation cost: ${total_cost:.3f}')
    print('Migration checklist:')
    print('  [x] Drop-in function created')
    print('  [x] Site-restricted search supported')
    print('  [x] Image search supported')
    print('  [x] Cost comparison calculated')
    print('  [x] Results validated')

validate_migration(['best crm software', 'python tutorial', 'site:github.com fastapi'])

Python Example

Python
import os, requests

API_KEY = os.environ['SCAVIO_API_KEY']

def google_search(query, num=10, gl='us'):
    """Drop-in replacement for Google CSE."""
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': gl})
    data = resp.json()
    items = [{'title': r['title'], 'link': r['link'], 'snippet': r.get('snippet', '')}
             for r in data.get('organic_results', [])[:num]]
    return {'items': items, 'searchInformation': {'totalResults': str(len(items))}}

results = google_search('best python frameworks 2026')
for item in results['items'][:3]:
    print(f'{item["title"]}: {item["link"]}')
print(f'Cost: $0.005 per query (CSE: $0.005 at paid tier, limited features)')

JavaScript Example

JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;

// Drop-in replacement for Google CSE
async function googleSearch(query, { num = 10, gl = 'us' } = {}) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: gl })
  });
  const data = await resp.json();
  const items = (data.organic_results || []).slice(0, num)
    .map(r => ({ title: r.title, link: r.link, snippet: r.snippet || '' }));
  return { items, searchInformation: { totalResults: String(items.length) } };
}

async function main() {
  const results = await googleSearch('best python frameworks 2026');
  results.items.slice(0, 3).forEach(item => {
    console.log(`${item.title}: ${item.link}`);
  });
}

main().catch(console.error);

Expected Output

JSON
CSE: q -> Scavio: query
CSE: gl -> Scavio: country_code

Found 8 results from stripe.com

1,000 queries: CSE $0.00 vs Scavio $5.00
5,000 queries: CSE $10.00 vs Scavio $25.00
20,000 queries: CSE $85.00 vs Scavio $100.00

Migration validation:
  "best crm software": New API: 10 results, top: Best CRM Software 2026
  [x] Drop-in function created
  [x] Results validated

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.

Python 3.9+ installed. Existing Google CSE integration to migrate. A Scavio API key from scavio.dev. requests library installed. 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 Google Custom Search Engine to a modern search API. Drop-in replacement functions, cost comparison, and testing guide.