Google Search Results in Python: 2026 API Access and Migration Limits
Google Search data collection changed materially in 2026: the Custom Search JSON API is closed to new customers, and Google gives existing customers until January 1, 2027 to migrate. Choose the data product before writing a scraper. Search Console metrics, a Programmable Search Engine response and an AI answer grounded in Google Search are different outputs.
Before writing the collector
- New customers should not build a project around obtaining Custom Search JSON API access.
- The Python example below is only for existing authorized customers before the stated migration deadline.
- Preserve the source product and result context; an API response is not automatically a reproduction of a consumer Google results page.
Define the result your application needs
Start with the question: do you need your siteโs search-performance metrics, results from a configured search engine, a grounded answer with sources, or a licensed observation of a search-results page? These differ in access, schema, coverage and billing.
Record the intended country, language, query and collection time where relevant. Do not merge distinct result types under a generic โGoogle rankingโ field. A useful dataset should make its measurement understandable to someone who did not run the collection.
Account for the January 2027 migration deadline
Googleโs Custom Search JSON API overview states that the service is closed to new customers and that existing customers have until January 1, 2027 to transition. The page identifies Vertex AI Search for searching up to 50 domains and provides a contact route for its full-web-search solution.
These are different evaluation paths, not a promise of immediate access to a drop-in replacement. Existing integrations need a migration decision before the deadline; a new project should establish its available product and terms before building against legacy examples.
The request example below supports maintenance and validation of an already authorized integration. It does not reopen enrollment or guarantee that a newly generated API key will work.
Confirm access to the specific data product
Check the selected serviceโs current account eligibility and permitted use. An API key identifies an application but does not establish access to every Google product. A third-party search-data service also needs review on its own terms and provenance.
A proxy is a network component. It does not grant API access, change an account quota or supply rights to reuse returned content. Keep the access decision separate from the later work of configuring the HTTP client. This article describes engineering choices and documented product limits, without providing a legal determination for a particular project.
Choose among performance data, search results and grounded answers
| Required output | Product to evaluate | Important boundary |
|---|---|---|
| Your propertyโs search-performance data | Search Console API | Authorized property; incomplete row coverage possible |
| Configured-engine result records | Existing Custom Search JSON API integration | Closed to new customers; migration deadline |
| AI answer supported by search sources | Gemini Google Search grounding | Generated answer and citations, not a rank export |
| Domain-limited application search | The Vertex AI Search option linked by Google | Verify scope and suitability before migration |
The Search Console Search Analytics API returns performance data for a specified property and date range. It warns that results are subject to internal limits and are not guaranteed to contain every row. Use it for authorized property analysis, not as a public export of competitor SERPs.
Grounding with Google Search in Gemini lets the model search when useful and return an answer with source citations. The current documentation includes search-call and search-result steps and citation annotations. That is an answer-generation workflow, not an ordered ranking feed.
For the legacy Custom Search API, the configured engine is part of the measurement. Preserve its identifier and settings. Do not assume that its result order matches a different userโs browser session on google.com.
Keep each collection run bounded
The cse.list method documents the request parameters and result limits. It allows at most ten results per request and never returns more than one hundred results for a query. The example asks for five and makes one request.
A limited result list is not a full crawl of all matching pages. If the task needs more coverage than the product provides, choose a suitable data source instead of treating a pagination limit as an invitation to enumerate unlimited query variants.
Set a job-level request budget and cancellation path. An AI workerโs retries and follow-up queries should consume that same budget rather than silently creating a separate source of traffic.
Python example for an existing Custom Search customer
Use an existing authorized API key and configured search engine. Supply GOOGLE_SEARCH_API_KEY and GOOGLE_SEARCH_ENGINE_ID through the runtimeโs secret mechanism. Save the code as google_search.py; install a supported Requests release in an isolated environment.
import json
import os
from datetime import datetime, timezone
import requests
URL = 'https://customsearch.googleapis.com/customsearch/v1'
def search_existing_engine(query, api_key, engine_id):
if not all(value.strip() for value in (query, api_key, engine_id)):
raise ValueError('Supply a query and an existing authorized engine/key')
try:
with requests.Session() as client:
client.trust_env = False
response = client.get(
URL, params={'q': query, 'key': api_key, 'cx': engine_id, 'num': 5},
timeout=(5, 20), allow_redirects=False,
)
except requests.RequestException:
# Request exception strings can contain the key-bearing URL.
raise RuntimeError('Search connection failed; inspect sanitized metrics') from None
if response.status_code != 200:
raise RuntimeError(f'Search stopped: HTTP {response.status_code}')
data = response.json()
if not isinstance(data, dict) or data.get('kind') != 'customsearch#search':
raise ValueError('Unexpected search response')
items = data.get('items', [])
if not isinstance(items, list):
raise ValueError('Unexpected search items')
observed = datetime.now(timezone.utc).isoformat()
rows = []
for position, item in enumerate(items, 1):
if not isinstance(item, dict) or not isinstance(item.get('link'), str):
raise ValueError('Search item has no URL')
rows.append({
'position_in_response': position, 'title': item.get('title'),
'url': item['link'], 'snippet': item.get('snippet'),
'engine_id': engine_id, 'observed_at': observed,
})
return rows
if __name__ == '__main__':
rows = search_existing_engine(
'proxy protocol documentation',
os.environ['GOOGLE_SEARCH_API_KEY'], os.environ['GOOGLE_SEARCH_ENGINE_ID'],
)
print(json.dumps(rows, ensure_ascii=False, indent=2))
The Search response schema identifies the response type and its items collection. The example checks that type, preserves missing optional fields and labels the ordinal as position_in_response. It does not call that value a universal organic ranking.
Environment proxy inheritance and redirects are disabled deliberately. If the authorized client needs a proxy, configure it explicitly as shown in the Requests guide.
Classify errors before deciding whether to retry
An access-denied response can reflect API eligibility, key restrictions or product permissions. A throttling response needs a quota-aware decision. A connection failure is a separate category. Do not report these failures as โno search results.โ
The example stops on non-200 responses and masks connection-exception text because that text can contain the key-bearing request URL. It does not follow redirects or automatically retry. Add a bounded, service-appropriate retry policy only after classifying the failure.
What the local tests establish
The exact example passed local fixtures with Python 3.12.14 and Requests 2.34.2. Checks covered the endpoint and prepared parameters, valid empty responses, malformed schemas, selected output fields, non-success responses, redirect refusal and removal of API keys from connection-error messages.
No live Google key or existing customer entitlement was used. These tests validate request construction and handling of representative responses; they do not establish production eligibility, current quota availability or result quality for a particular engine.
Budget the product you actually call
Googleโs legacy overview lists 100 free queries per day for existing customers, then $5 per 1,000 queries up to 10,000 daily, with the stated discontinuation date. Those figures are not a new-customer offer and should not be carried into a replacement productโs forecast.
Gemini grounding has its own billing and usage conditions. Check the model and current terms before estimating a run; a model can decide to issue multiple search queries. Count useful, verified outputs alongside API requests so a generated answer that needs correction does not look free.
Keep keys and source content out of the wrong channel
Do not log full request URLs when they contain API keys. Restrict access to secrets and rotate them through the accountโs supported process. A report usually needs the query, source product, timestamp and sanitized status, not the credential.
Returned snippets and pages are untrusted content. They should not change the workerโs target, permissions or secret settings. If an AI tool summarizes the response, validate its claims against the returned sources and keep the citations attached to the supported statements.
Test the replacement against the same requirement
For migration, compare output semantics before comparing speed. Check domain coverage, result fields, localization, freshness, citation format, quota handling and cost. Store source product names explicitly so downstream reports can distinguish old and new observations.
Run a small parallel comparison only within the access you already have. Inspect mismatches manually and document which differences come from product design rather than treating every changed result as a regression. Keep an owner and date for the migration decision.
Record the source and collector version
Keep the source product, collection time, query parameters and collector version with each observation. When a migration changes the result format or coverage, mark that boundary in reports so a product change is not mistaken for a change in search visibility.
For work involving AI-generated search answers, continue with the AI Overviews monitoring discussion as a separate workflow topic. For orchestration, the n8n proxy guide helps identify the process sending requests. Neither link changes the access requirements described above.
Sources and review scope
Sources reviewed October 11, 2026. Primary documentation checked October 11, 2026. Examples passed local fixtures; the Indeed CSV example also ran against its public dataset. No private API credentials or live host calendar were used. Each guide states its access and coverage limits.
Frequently asked questions
Collect from another source
Choose an available data product and preserve its coverage, permissions and field meanings.
Related workflows
Configure the HTTP worker without leaking keys.
Keep answer visibility separate from ordinary result records.
Apply routing to the node making the request.
Where to get a dedicated line
A Coronium mobile proxy is one physical device with its own SIM, rented to one customer. It connects over HTTP or SOCKS5, the IP changes by dashboard button, API link or timer, and data is not metered on dedicated lines. Stock and prices differ by country and carrier.