cd /news/developer-tools/google-s-custom-search-api-shuts-dow… · home › topics › developer-tools › article
[ARTICLE · art-141926] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Google's Custom Search API shuts down Jan 1. Migrate by changing one URL.

A developer built cse-compat, an open-source Cloudflare Worker that preserves the Google Custom Search JSON API contract ahead of the API's January 1, 2027 shutdown. The tool serves the same /customsearch/v1 endpoint, parameters, response fields and error format while proxying queries to providers like Serper or Brave using the user's own API key, so existing clients only need to change the host and key. It is released under Apache-2.0 and runs on Cloudflare's free plan.

by read5 min views3 publishedSep 29, 2026

If you have code that calls https://www.googleapis.com/customsearch/v1, it stops working on January 1, 2027. Google has already closed the API to new customers, and existing ones are being moved off it.

I went through this myself and ended up building a small open-source tool for it. This post covers what's changing, what your options are, and the approach I went with, including where it falls short.

The Custom Search JSON API is the one where you send key, cx and q, and get back JSON with an items array. For years it was the easy way to put web search into a script, an internal tool, or more recently an AI agent: 100 free queries a day, then $5 per 1,000.

Google's suggested replacements aren't the same product:

So if you were using it for general web search, there's no official drop-in path.

1. Rewrite for a new search API. Brave, Serper, Exa, Tavily and others all have good APIs. But each one returns a different JSON shape, so you rewrite your parsing, pagination and error handling. That's fine for one small script, and painful if the call is spread across several services or buried inside a library you don't own.

2. Switch to Vertex AI Search. Makes sense if you were only ever searching your own sites. It's a different API, though, so it's still a rewrite.

3. Keep the old API shape and swap what's behind it. Put a thin layer in front of a new provider that speaks the old format exactly. Your code keeps calling the same endpoint shape, you change the base URL, and that's it.

I went with option 3.

If you try to build a compatibility layer yourself, "return some JSON with items" isn't enough. Code that has run against this API for years quietly depends on details like:

items is missing entirely when there are no results.if "items" in res. htmlSnippet and htmlTitle``<b> tags around matched words, and some UIs render them directly.queries.nextPage`` start + num can't go past 100, and 429 with RESOURCE_EXHAUSTED. A new provider returning 402 Payment Required hits a code path your client has never seen.totalResults is a string Getting these right is most of the work.

cse-compat is a small Cloudflare Worker that serves /customsearch/v1 with the old contract: same parameters, same response fields, same error format. Behind it, it calls a search provider using your own API key (Serper today, with a Brave adapter included). It's open source under Apache-2.0 and runs fine on Cloudflare's free plan.

You can try the public demo without signing up:

curl "https://csecompat.com/customsearch/v1?key=demo&cx=test&q=hello"

(The demo has a daily cap, so for real use you deploy your own copy.)

git clone https://github.com/csecompat/cse-compat.git && cd cse-compat
npm install
npx wrangler secret put SERPER_API_KEY
npx wrangler secret put PROXY_KEYS
npx wrangler deploy

SERPER_API_KEY is your key from serper.dev. PROXY_KEYS is a key you make up, which your apps send as ?key= instead of the old Google key.

Only the host and the key change:

- https://www.googleapis.com/customsearch/v1?key=GOOGLE_KEY&cx=YOUR_CX&q=best+pizza
+ https://cse.yourdomain.workers.dev/customsearch/v1?key=YOUR_PROXY_KEY&cx=YOUR_CX&q=best+pizza

The official client has Google's host built in, but it lets you override it:

from googleapiclient.discovery import build

service = build(
    "customsearch", "v1",
    developerKey=CSE_COMPAT_KEY,
    client_options={"api_endpoint": "https://cse.yourdomain.workers.dev"},
    static_discovery=True,
)

res = service.cse().list(q="lectures", cx=CX, num=10).execute()
for item in res.get("items", []):
    print(item["title"], item["link"])

static_discovery=True makes the client use its built-in API description instead of fetching it from Google, so nothing depends on Google's servers after the shutdown. There's a longer guide here.

const { google } = require('googleapis');

const customsearch = google.customsearch({
  version: 'v1',
  rootUrl: 'https://cse.yourdomain.workers.dev',
});

const res = await customsearch.cse.list({ auth: API_KEY, cx: CX, q: 'lectures' });
// res.data.items works as before

Go's client has option.WithEndpoint(...) for the same thing, and Java has setRootUrl.

Google CSE cse-compat
Request and response format original same
Error format original same
Ranking Google's index your provider's
Image search ( searchType=image ) yes not yet (returns a clear error)
pagemap data yes not yet
CSE console features (promotions, refinements) yes no

A few more things worth knowing:

If you still have a working CSE key, save some real responses now. After January 1 nobody can generate new ones, and they're the best way to check that any replacement, mine or not, behaves like the original.

The repo has a script that captures a set of real responses and removes your key from them:

GOOGLE_API_KEY=... GOOGLE_CX=... node scripts/capture-fixtures.mjs

A second script then compares any deployment against those captures, checking field by field that the shape matches:

node scripts/diff-golden.mjs https://cse.yourdomain.workers.dev YOUR_PROXY_KEY

If you'd like to contribute captured fixtures to the repo, I'd really appreciate it.

If your code is one small script, rewriting it for a new provider is probably simplest. If the Custom Search call is spread across services, or inside libraries you don't control, keeping the API shape and swapping what's behind it can save a lot of work.

The repo is github.com/csecompat/cse-compat. Issues and PRs are welcome, and I'd love to hear what you use the Custom Search API for. I'm also considering a hosted version for teams that don't want to run their own worker. There's a waitlist on csecompat.com if that's you.

── more in #developer-tools 4 stories · sorted by recency
── more on @google 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/google-s-custom-sear…] indexed:0 read:5min 2026-09-29 · —