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
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.
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.
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.
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
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"])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
| 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
| 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
{
"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
cxdoes nothing. If your engine was restricted to a list of sites in the Programmable Search console, recreate it withsiteSearch(one site) orsite:a.com OR site:b.cominq.- 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. Thekeyquery 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
429with theRetry-Afterheader. - Errors use SerpKite's error body
{"error":{"code","message","request_id"}}, not Google'serror.errors[]. If you parse Google error reasons, switch onerror.codeinstead. - Need more than the CSE fields? The native
POST /v1/searchreturnsresults,people_also_ask,knowledge_graph,answer_boxand Markdown output with the same key. With the Python SDK it isSerpKite().search("site:docs.python.org asyncio", country="us"). See the Google Search API.
Checklist
Step by step
-
1
Create a free account and copy your
skt_live_…key from the dashboard. -
2
Replace
https://www.googleapis.com/customsearch/v1withhttps://api.serpkite.com/customsearch/v1. -
3
Replace your Google API key with the SerpKite key (or send it as
Authorization: Bearer). -
4
If your engine was site-restricted, add
siteSearchorsite:operators to the query. -
5
Run your test suite or a handful of real queries and compare
items[].linkandtitle. -
6
Update error handling for
401,402,429and503with the SerpKite error codes. Failed calls are not billed and are safe to retry. - 7 Set a monthly spend cap and a low-balance alert in the dashboard, then deploy before January 1, 2027.
-
8
Optional, later: move to
POST /v1/searchor 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
| 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.