googlesearch-apimigration

Google Programmable Search Engine Shutting Down: Alternatives

Google PSE disabling web-wide search by Jan 2027. Compare Brave, Tavily, Serper, and Scavio as replacements.

7 min

Google is disabling Programmable Search Engine (PSE) for web-wide searches by January 2027. If your application, local LLM, or agent relies on PSE for general web search, you need to migrate to an alternative API before the cutoff.

What Is Changing

Google Programmable Search Engine allowed developers to create custom search engines that searched the entire web, not just specified sites. The announcement restricts PSE to site-restricted searches only. This means you can still search within domains you configure, but you can no longer use PSE as a general-purpose web search API. For many developers, especially those using PSE to ground LLMs and AI agents with live web data, this is a breaking change.

Who Is Affected

  • Local LLM setups using PSE for retrieval-augmented generation (RAG)
  • AI agents that call PSE for real-time web search grounding
  • Apps built on the free tier of PSE (100 queries/day free, 10K queries/$5)
  • Research tools that aggregate web results across arbitrary domains

Reddit threads on r/LocalLLM show significant concern. Many developers chose PSE specifically because it was free or near-free and returned Google-quality results. No alternative matches that exact combination.

Alternative APIs Compared

  • Brave Search API: free 2,000 queries/month. Good result quality. No SERP features (PAA, Knowledge Graph). Limited to web results.
  • Tavily: free 1,000 credits. $30/month Researcher, $100/month Startup. $0.008/credit pay-as-you-go. Designed for AI agents. Returns AI-extracted answers alongside raw results.
  • Serper: free 2,500/month. $50/month for 500K queries ($0.10/1K). Google results only. Fast. Good for high-volume Google-only use cases.
  • Scavio: free 250 credits/month. $0.005/credit on-demand. $30/month for 7K credits. Covers Google, Amazon, YouTube, Reddit, TikTok, Google Maps. Returns structured JSON with AI Overviews, PAA, Knowledge Graph. MCP server included.

Migration: PSE to Scavio

Python
# Before: Google Programmable Search Engine
# from googleapiclient.discovery import build
# service = build("customsearch", "v1", developerKey=API_KEY)
# result = service.cse().list(q="query", cx=CSE_ID).execute()

# After: Scavio Search API
import requests, os

API_KEY = os.environ["SCAVIO_API_KEY"]

def web_search(query: str, country: str = "us") -> dict:
    """Drop-in replacement for PSE web search."""
    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": country})
    return resp.json()

results = web_search("latest python 3.14 release notes")
for item in results.get("organic_results", [])[:5]:
    print(f"{item['position']}. {item['title']}")
    print(f"   {item['link']}")
    print(f"   {item.get('snippet', '')[:100]}")
    print()

Migration: PSE to Brave Search

Python
import requests, os

BRAVE_KEY = os.environ["BRAVE_API_KEY"]

def brave_search(query: str, count: int = 10) -> dict:
    """Brave Search API - free 2K queries/month."""
    resp = requests.get("https://api.search.brave.com/res/v1/web/search",
        headers={"X-Subscription-Token": BRAVE_KEY},
        params={"q": query, "count": count})
    return resp.json()

results = brave_search("latest python 3.14 release notes")
for item in results.get("web", {}).get("results", [])[:5]:
    print(f"{item['title']}: {item['url']}")

For LLM and Agent Users

If you were using PSE to ground a local LLM or AI agent, the migration is straightforward. Replace the PSE API call with any of the alternatives above. The key difference: PSE returned a specific JSON schema. Each alternative has its own schema. Plan for a thin adapter layer.

Python
def normalize_results(raw: dict, provider: str) -> list:
    """Normalize search results from different providers."""
    if provider == "scavio":
        return [{"title": r["title"], "url": r["link"],
                 "snippet": r.get("snippet", "")}
                for r in raw.get("organic_results", [])]
    elif provider == "brave":
        return [{"title": r["title"], "url": r["url"],
                 "snippet": r.get("description", "")}
                for r in raw.get("web", {}).get("results", [])]
    elif provider == "pse":
        return [{"title": r["title"], "url": r["link"],
                 "snippet": r.get("snippet", "")}
                for r in raw.get("items", [])]
    return []

# Your LLM grounding code stays the same
# results = normalize_results(raw_data, "scavio")
# context = "\n".join(f"{r['title']}: {r['snippet']}" for r in results)
# llm_response = llm.generate(f"Based on: {context}\n\nQuestion: ...")

Cost Comparison for PSE Users

PSE gave 100 free queries/day (about 3,000/month) and charged $5 per 1,000 after that. At 5,000 queries/month, PSE cost $10. Brave free tier covers 2,000 queries. Serper free tier covers 2,500. Scavio free tier covers 250, but the $30/month plan gives 7,000 queries with structured SERP features that PSE never offered (AI Overviews, PAA, Shopping).

Timeline and Action Items

  • Now: audit your PSE usage volume and which response fields you depend on
  • Test alternatives with your actual queries to compare result quality
  • Build a provider abstraction layer so you can switch again if needed
  • Migrate before January 2027 when web-wide PSE stops working
  • If you only need site-restricted search, PSE continues to work for that use case