Tutorial

How to Replace Google Custom Search Engine Before 2027

Google CSE ends web-wide search Jan 2027. Emergency migration guide to replace CSE with a multi-platform search API in under an hour.

Google Custom Search Engine is closed to new signups and its search entire web capability ends January 1, 2027. If you are reading this, your migration deadline is approaching. This is the emergency migration guide: swap your CSE calls for Scavio API calls in under an hour. Scavio provides Google-equivalent results plus Amazon, YouTube, Walmart, Reddit, and TikTok from the same endpoint. At $0.005 per request with 250 free monthly credits, you likely save money compared to CSE pricing.

Prerequisites

  • A working CSE integration you need to replace
  • Python 3.9+ or Node.js 18+ installed
  • A Scavio API key from scavio.dev (free tier: 250 credits/month)
  • 30-60 minutes for the migration

Walkthrough

Step 1: Map your CSE response fields to Scavio

The critical difference: CSE uses GET with query params, Scavio uses POST with JSON body. Response field mapping: items -> organic_results, searchInformation -> search_metadata, queries -> not needed.

Python
# CSE response structure -> Scavio equivalent
field_mapping = {
    # CSE field              -> Scavio field
    'items':                    'organic_results',
    'items[].title':            'organic_results[].title',
    'items[].link':             'organic_results[].link',
    'items[].snippet':          'organic_results[].snippet',
    'items[].pagemap':          'organic_results[].rich_snippet',
    'searchInformation.totalResults': 'search_metadata.total_results',
    'spelling.correctedQuery':  'search_metadata.spelling_fix',
    'queries.nextPage':         'pagination.next',
}

# CSE parameter mapping
param_mapping = {
    # CSE param    -> Scavio equivalent
    'q':            'query',
    'num':          'num_results',
    'gl':           'country_code',
    'hl':           'language',
    'start':        'page (via pagination)',
    'siteSearch':   'query prefix: site:domain.com',
    'dateRestrict': 'query suffix: after:YYYY-MM-DD',
}

for cse, scavio in field_mapping.items():
    print(f'{cse:45s} -> {scavio}')

Step 2: Write the drop-in replacement function

Create a function with the same signature as your CSE wrapper. This handles the HTTP method change (GET to POST) and response field renaming.

Python
import os, requests

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']

def google_search(query: str, num: int = 10, country: str = 'us', **kwargs) -> dict:
    """Drop-in CSE replacement. Same return shape as CSE response."""
    # Handle site restriction
    site = kwargs.get('siteSearch', '')
    if site:
        query = f'site:{site} {query}'
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': country, 'num_results': num})
    resp.raise_for_status()
    data = resp.json()
    # Return CSE-compatible structure
    organic = data.get('organic_results', [])
    return {
        'items': [{'title': r['title'], 'link': r['link'],
                   'snippet': r.get('snippet', ''),
                   'displayLink': r['link'].split('/')[2] if '/' in r['link'] else ''}
                  for r in organic],
        'searchInformation': {'totalResults': str(len(organic))}
    }

result = google_search('python tutorial 2026')
print(f"Got {len(result['items'])} results")
for item in result['items'][:3]:
    print(f"  {item['title']}")

Step 3: Find and replace all CSE calls

Search your codebase for CSE API calls and swap them. Common patterns include direct HTTP calls, Google API client library usage, and third-party CSE wrappers.

Python
# Pattern 1: Direct requests call to CSE
# BEFORE:
# resp = requests.get('https://www.googleapis.com/customsearch/v1',
#     params={'key': CSE_KEY, 'cx': CSE_ID, 'q': query})
# AFTER:
resp = requests.post('https://api.scavio.dev/api/v1/search',
    headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
    json={'query': query, 'country_code': 'us', 'num_results': 10})

# Pattern 2: Google API Client Library
# BEFORE:
# from googleapiclient.discovery import build
# service = build('customsearch', 'v1', developerKey=CSE_KEY)
# result = service.cse().list(q=query, cx=CSE_ID).execute()
# AFTER:
result = google_search(query)  # Use the wrapper from step 2

# Pattern 3: Environment variable cleanup
# Remove: GOOGLE_CSE_KEY, GOOGLE_CSE_ID
# Add: SCAVIO_API_KEY
import os
assert os.environ.get('SCAVIO_API_KEY'), 'Set SCAVIO_API_KEY env var'
print('Environment configured')

Step 4: Run a side-by-side validation

Test your most common queries through both the old and new paths. Verify that result count, top results, and any fields you parse downstream all look correct.

Python
def validate_migration(queries: list[str]):
    print('Migration Validation Report')
    print('=' * 50)
    all_pass = True
    for q in queries:
        result = google_search(q)
        items = result.get('items', [])
        checks = {
            'has_results': len(items) > 0,
            'has_titles': all(i.get('title') for i in items),
            'has_links': all(i.get('link') for i in items),
            'has_snippets': all(i.get('snippet') for i in items[:3]),
        }
        status = 'PASS' if all(checks.values()) else 'FAIL'
        if status == 'FAIL':
            all_pass = False
        print(f'\n[{status}] {q}')
        print(f'  Results: {len(items)}')
        for check, passed in checks.items():
            print(f'  {check}: {"ok" if passed else "FAILED"}')
    print(f'\nOverall: {"ALL PASS" if all_pass else "SOME FAILURES"}')

validate_migration([
    'python web framework 2026',
    'machine learning tutorial',
    'best laptop for coding',
])

Step 5: Update environment and deploy

Remove CSE credentials, add Scavio key, and deploy. The free tier gives you 250 requests per month to verify in production before committing to a paid plan.

Python
# .env file update
# REMOVE these:
# GOOGLE_CSE_API_KEY=AIza...
# GOOGLE_CSE_ENGINE_ID=017...

# ADD this:
# SCAVIO_API_KEY=your_key_here

# Verify the migration
import os

def verify_deployment():
    key = os.environ.get('SCAVIO_API_KEY')
    assert key, 'SCAVIO_API_KEY not set'
    # Quick health check
    result = google_search('test query')
    assert len(result['items']) > 0, 'No results returned'
    print('Migration verified:')
    print(f'  API key: {key[:8]}...')
    print(f'  Test query returned {len(result["items"])} results')
    print(f'  Cost per request: $0.005')
    print(f'  Free tier: 250 requests/month')
    print(f'  Paid plan: $30/month for 7,000 credits')
    # Cost comparison
    print(f'\nCSE was: $5 per 1,000 queries')
    print(f'Scavio: $5 per 1,000 queries (same price, more platforms)')

verify_deployment()

Python Example

Python
import os, requests

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']

def google_search(query, num=10, **kw):
    site = kw.get('siteSearch', '')
    if site:
        query = f'site:{site} {query}'
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us', 'num_results': num})
    resp.raise_for_status()
    results = resp.json().get('organic_results', [])
    return {'items': [{'title': r['title'], 'link': r['link'],
            'snippet': r.get('snippet', '')} for r in results]}

result = google_search('best python frameworks 2026')
for item in result['items'][:5]:
    print(f"{item['title']}\n  {item['link']}")
print(f'\nCost: ${len(result["items"]) * 0.005:.3f} for this search')

JavaScript Example

JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;

async function googleSearch(query, { num = 10, siteSearch = '' } = {}) {
  if (siteSearch) query = `site:${siteSearch} ${query}`;
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: 'us', num_results: num })
  });
  const data = await resp.json();
  return { items: (data.organic_results || []).map(r => ({
    title: r.title, link: r.link, snippet: r.snippet || ''
  }))};
}

googleSearch('best python frameworks 2026').then(r => {
  r.items.slice(0, 5).forEach(i => console.log(`${i.title}\n  ${i.link}`));
});

Expected Output

JSON
Migration Validation Report
==================================================

[PASS] python web framework 2026
  Results: 10
  has_results: ok
  has_titles: ok
  has_links: ok
  has_snippets: ok

[PASS] machine learning tutorial
  Results: 10

Overall: ALL PASS

Migration verified:
  API key: sk-12345...
  Test query returned 10 results
  Cost per request: $0.005

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.

A working CSE integration you need to replace. Python 3.9+ or Node.js 18+ installed. A Scavio API key from scavio.dev (free tier: 250 credits/month). 30-60 minutes for the migration. 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

Google CSE ends web-wide search Jan 2027. Emergency migration guide to replace CSE with a multi-platform search API in under an hour.