Skip to content

New Official SDKs for TypeScript, Python and Go

SerpKite
Get API key

Migration guide

Migrate from the Google Custom Search JSON API

Google closed the Custom Search JSON API to new customers on January 20, 2026 and shuts it down on January 1, 2027. SerpKite serves the same endpoint shape at /customsearch/v1, so most apps only change the hostname and the key.

Custom Search JSON API shutdown:

91 days left. After that date, requests to the old endpoint fail.

Why switch

Why teams move from Google Custom Search JSON API to SerpKite

01

There is no official successor

Google's partner-only Web Search Service API briefly appeared in docs in Aug/Sep 2026 and was pulled. There is no self-serve official Google web search API after January 1, 2027.

02

Same shape, same parameters

key, cx, q, start, num, gl, hl, lr, safe, dateRestrict, siteSearch and searchType=image are accepted. Responses keep kind, searchInformation, items[] and queries.

03

Results from the whole web

Results come from the public Google results page, the same page a logged-out user sees. Restrict to your domains with siteSearch or a site: operator.

04

Credits never expire

Buy a pack once. No daily cap, no monthly reset, and failed or empty searches are not billed.

The change

Before and after

cse.py (Google) −2 lines
import os, requests

res = requests.get(
    "https://www.googleapis.com/customsearch/v1",
    params={
        "key": os.environ["GOOGLE_API_KEY"],
        "cx": os.environ["GOOGLE_CSE_ID"],
        "q": "site:docs.python.org asyncio",
        "start": 1,
        "num": 10,
    },
    timeout=30,
)
for item in res.json().get("items", []):
    print(item["title"], item["link"])
cse.py (SerpKite) +2 lines
import os, requests

res = requests.get(
    "https://api.serpkite.com/customsearch/v1",
    params={
        "key": os.environ["SERPKITE_API_KEY"],
        "cx": os.environ["GOOGLE_CSE_ID"],
        "q": "site:docs.python.org asyncio",
        "start": 1,
        "num": 10,
    },
    timeout=30,
)
for item in res.json().get("items", []):
    print(item["title"], item["link"])

Two lines change: the hostname and the key. cx is accepted and ignored, so you can leave it in place while you migrate. When you want People Also Ask, knowledge graph or Markdown output, move to the native POST /v1/search endpoint or the official SDKs (npm install serpkite, pip install serpkite).

Reference

Parameter and field mapping

Request parameters

Request parameter mapping
Google Custom Search JSON API SerpKite Notes
key key Your skt_live_… key. Authorization: Bearer skt_live_… also works and keeps keys out of URLs and logs.
cx cx Accepted and ignored. Search engine settings from the Programmable Search console are not applied.
q q Same. Operators like site:, -term and "quotes" work as on google.com.
start start 1 to 91, as with CSE. 10 results per page.
num num 1 to 10. Each call costs 1 credit whatever num is.
gl / hl / lr gl / hl / lr Country, interface language and language restrict.
safe safe active or off.
dateRestrict dateRestrict d7, w2, m6, y1…
siteSearch siteSearch Restrict to one site. Use it to recreate a site-restricted engine.
searchType=image searchType=image Image results in the CSE items[] shape.

Response fields

Response field mapping
Google Custom Search JSON API SerpKite Notes
kind kind customsearch#search.
searchInformation searchInformation searchTime, formattedSearchTime, totalResults, formattedTotalResults.
items[].title / htmlTitle items[].title / htmlTitle
items[].link items[].link Always the resolved destination URL, never a Google redirect.
items[].displayLink / formattedUrl items[].displayLink / formattedUrl
items[].snippet / htmlSnippet items[].snippet / htmlSnippet
items[].pagemap items[].pagemap Present when the result page exposes structured data. Treat it as optional, as with CSE.
queries.request / nextPage queries.request / nextPage

Live example

Custom Search (CSE-compatible): request and response

curl "https://api.serpkite.com/customsearch/v1?q=site%3Adocs.python.org+asyncio&cx=any&start=1&num=10" \
  -H "Authorization: Bearer $SERPKITE_API_KEY"

1 credit per call. Failed and empty results are free. Run it in the playground

200 OK (abridged)
{
  "kind": "customsearch#search",
  "searchInformation": {
    "searchTime": 0.94,
    "formattedSearchTime": "0.94",
    "totalResults": "1240000",
    "formattedTotalResults": "1,240,000"
  },
  "items": [
    {
      "kind": "customsearch#result",
      "title": "asyncio — Asynchronous I/O",
      "htmlTitle": "<b>asyncio</b> — Asynchronous I/O",
      "link": "https://docs.python.org/3/library/asyncio.html",
      "displayLink": "docs.python.org",
      "snippet": "asyncio is a library to write concurrent code using the async/await syntax."
    }
  ],
  "queries": {
    "request": [
      {
        "searchTerms": "site:docs.python.org asyncio",
        "count": 10,
        "startIndex": 1
      }
    ]
  }
}

Watch out

Gotchas

  • cx does nothing. If your engine was restricted to a list of sites in the Programmable Search console, recreate it with siteSearch (one site) or site:a.com OR site:b.com in q.
  • Results are full Google, ranked as google.com ranks them. A CSE with custom ranking, refinements or promotions will look different.
  • Keys in URLs end up in logs. Prefer Authorization: Bearer skt_live_… as a header. The key query parameter is supported for drop-in compatibility with CSE.
  • No 10,000-a-day cap, but there is a per-second rate limit per plan (5 requests/second on the free tier). Handle 429 with the Retry-After header.
  • Errors use SerpKite's error body {"error":{"code","message","request_id"}}, not Google's error.errors[]. If you parse Google error reasons, switch on error.code instead.
  • Need more than the CSE fields? The native POST /v1/search returns results, people_also_ask, knowledge_graph, answer_box and Markdown output with the same key. With the Python SDK it is SerpKite().search("site:docs.python.org asyncio", country="us"). See the Google Search API.

Checklist

Step by step

  1. 1 Create a free account and copy your skt_live_… key from the dashboard.
  2. 2 Replace https://www.googleapis.com/customsearch/v1 with https://api.serpkite.com/customsearch/v1.
  3. 3 Replace your Google API key with the SerpKite key (or send it as Authorization: Bearer).
  4. 4 If your engine was site-restricted, add siteSearch or site: operators to the query.
  5. 5 Run your test suite or a handful of real queries and compare items[].link and title.
  6. 6 Update error handling for 401, 402, 429 and 503 with the SerpKite error codes. Failed calls are not billed and are safe to retry.
  7. 7 Set a monthly spend cap and a low-balance alert in the dashboard, then deploy before January 1, 2027.
  8. 8 Optional, later: move to POST /v1/search or the official TypeScript, Python or Go SDK for People Also Ask, knowledge graph and Markdown output.

Pricing

SerpKite vs Google Custom Search JSON API: price

Price comparison with Google Custom Search JSON API
SerpKite Google Custom Search JSON API
Free tier 2,500 on signup + 1,000/mo 100 queries/day
List price $1.00 → $0.60 per 1k $5.00 per 1k
Daily cap None (rate limit per second) 10,000 queries/day
Credit expiry Never Billed monthly
Available after Jan 1, 2027 Yes No

Google's own list price for the JSON API was $5 per 1,000 queries after 100 free queries a day, with a 10,000-a-day cap. SerpKite has no daily cap; each /customsearch/v1 call costs 1 credit. SerpKite packs: $1.00 per 1k on the $10 pack down to $0.60 on the largest self-serve pack; larger volumes on request, enterprise from $0.18. Competitor list prices verified on vendor pages on 2026-09-28. See pricing.

FAQ

Migrating from Google Custom Search JSON API

Start building

Leave Google Custom Search JSON API today

2,500 free credits, then 1,000 every month. No credit card.