Grounding Gemini with Google Search
Large language models (LLMs) like Gemini are incredibly powerful, but they have a fundamental limitation: their knowledge is frozen at the time they were trained. They don't have access to live, real-time information from the internet. This means if you ask about today's news, stock prices, or the weather, they can't give you a current answer.
This is where grounding comes in. Grounding connects the model to external, authoritative sources of information, like Google Search. By giving Gemini the ability to search the web, we can unlock its potential to answer questions about the here and now, ensuring its responses are timely, accurate, and verifiable. 🌐
Disclaimers:
- At the time of this writing, I am employed by Google Cloud. However the thoughts expressed here are my own and do not represent my employer.
- The code provided here is sample code for educational purposes only. Please write your own production code.
- Google has detailed terms of service for using Grounding with Google Search. Please refer to the Google Documentation before adding Grounding with Google Search to any application.
What We're building
To demonstrate the power of grounding, we'll build a simple Python script that asks Gemini a question it can't answer on its own: "What is the weather in San Francisco?" 🌦️
We'll see how the model responds first without and then with the Google Search tool enabled.
Prerequisites
-
Google Gen AI SDK installed: If you haven't already, install the library.
pip install google-genai
-
Authentication: Your environment should be authenticated. For details on how to set up Application Default Credentials or use a service account, check out our previous guide on authenticating the Google Gen AI SDK.
Constructing the request
Let's start by asking Gemini for the weather without giving it any special tools.
from google.genai import Client, types
client = Client(vertexai=True, project='<your-gcp-project-id>', location='global')
response = client.models.generate_content(
model='gemini-2.5-flash-lite',
contents="what is the weather in San Francisco?"
)
print(response.text)
As expected, the model is helpful but can't provide a real-time answer because its internal knowledge is static. It correctly identifies that it needs live data and suggests alternative ways to find it.
Response:
I'm sorry, I don't have access to real-time weather information. My knowledge cutoff is **June 2024**, and I cannot provide current weather updates.
To get the most up-to-date weather for San Francisco, I recommend using one of the following:
* **A weather app on your phone:** Most smartphones come with a built-in weather app.
* **A weather website:** Popular options include:
* The Weather Channel: [https://weather.com/](https://weather.com/)
* AccuWeather: [https://www.accuweather.com/](https://www.accuweather.com/)
* National Weather Service: [https://www.weather.gov/](https://www.weather.gov/)
* **A quick web search:** Simply search "weather San Francisco" on your preferred search engine.
This response highlights the exact problem grounding solves. To get a real-time answer, we need to give the model a tool it can use.
Adding the Google Search Tool
Let's modify our request to include the built-in Google Search tool. This is done by adding a config
parameter to our generate_content
method.
from google.genai import Client, types
client = Client(vertexai=True, project='<your-gcp-project-id>', location='global')
response = client.models.generate_content(
model='gemini-2.5-flash-lite',
contents="what is the weather in San Francisco?",
config=types.GenerateContentConfig(
tools=[
types.Tool(google_search=types.GoogleSearch())
]
)
)
print(response.text)
Now, when we run the code, we get a completely different and much more useful answer. ✨
Response:
The weather in San Francisco is currently partly cloudy with a temperature of 65°F (18°C). The humidity is around 80%.
Here's a look at the forecast for the next few days:
* **Saturday (Today):** Partly cloudy during the day, becoming mostly cloudy at night. High near 67°F (19°C), low around 59°F (15°C). 10% chance of rain.
* **Sunday:** Mostly cloudy during the day, partly cloudy at night. High near 67°F (19°C), low around 61°F (16°C). 10% chance of rain.
* **Monday:** Mostly cloudy during the day with a chance of light rain at night. High near 68°F (20°C), low around 63°F (17°C). 20-25% chance of rain.
Some forecasts indicate a chance of showers arriving next week.
So, what happened behind the scenes? By providing theTool
, we gave Gemini a new capability. The model was smart enough to understand that the user's question required real-time information. It then automatically executed a Google Search for "weather in San Francisco", analyzed the search results, and synthesized the information into the clear, human-readable summary you see above.
Examining the response
The text response is great, but the SDK gives us much more information about how the model arrived at its answer. We can inspect the full response object to see the grounding metadata.
print(response.model_dump_json(indent=4))
This will output a large JSON object. If you scroll down, you'll find a grounding_metadata
key. This section is a treasure trove of information.
{
"sdk_http_response": {
"headers": {
"content-type": "application/json; charset=UTF-8",
"vary": "Origin, X-Origin, Referer",
"content-encoding": "gzip",
"date": "Sun, 07 Sep 2025 01:50:43 GMT",
"server": "scaffolding on HTTPServer2",
"x-xss-protection": "0",
"x-frame-options": "SAMEORIGIN",
"x-content-type-options": "nosniff",
"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
"transfer-encoding": "chunked"
},
"body": null
},
"candidates": [
{
"content": {
"parts": [
{
"video_metadata": null,
"thought": null,
"inline_data": null,
"file_data": null,
"thought_signature": null,
"code_execution_result": null,
"executable_code": null,
"function_call": null,
"function_response": null,
"text": "The weather in San Francisco is currently partly cloudy with a temperature of 65°F (18°C). The humidity is around 80%.\n\nHere's a look at the forecast for the next few days:\n\n* **Saturday (Today):** Partly cloudy during the day, becoming mostly cloudy at night. High near 67°F (19°C), low around 59°F (15°C). 10% chance of rain.\n* **Sunday:** Mostly cloudy during the day, partly cloudy at night. High near 67°F (19°C), low around 61°F (16°C). 10% chance of rain.\n* **Monday:** Mostly cloudy during the day with a chance of light rain at night. High near 68°F (20°C), low around 63°F (17°C). 20-25% chance of rain.\n\nSome forecasts indicate a chance of showers arriving next week."
}
],
"role": "model"
},
"citation_metadata": null,
"finish_message": null,
"token_count": null,
"finish_reason": "STOP",
"url_context_metadata": null,
"avg_logprobs": null,
"grounding_metadata": {
"google_maps_widget_context_token": null,
"grounding_chunks": [
{
"maps": null,
"retrieved_context": null,
"web": {
"domain": "google.com",
"title": "Weather information for San Francisco, CA, US",
"uri": "https://www.google.com/search?q=weather+in+San Francisco, CA,+US"
}
},
{
"maps": null,
"retrieved_context": null,
"web": {
"domain": "ktvu.com",
"title": "ktvu.com",
"uri": "https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFzPU84p7PN9wO8ix7pJme7cSH8007E76fuFW0XYvv1dlj7yMibz7I8ZIIV17MhwA4ECL0eKvj9afdQC6GaErqGdsWK2Fo4Nkgp9QYQ3fXAFUXTYt3ofdE="
}
}
],
"grounding_supports": [
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 92,
"part_index": null,
"start_index": null,
"text": "The weather in San Francisco is currently partly cloudy with a temperature of 65°F (18°C)."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 264,
"part_index": null,
"start_index": 176,
"text": "* **Saturday (Today):** Partly cloudy during the day, becoming mostly cloudy at night."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 315,
"part_index": null,
"start_index": 265,
"text": "High near 67°F (19°C), low around 59°F (15°C)."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 335,
"part_index": null,
"start_index": 316,
"text": "10% chance of rain."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 456,
"part_index": null,
"start_index": 406,
"text": "High near 67°F (19°C), low around 61°F (16°C)."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 476,
"part_index": null,
"start_index": 457,
"text": "10% chance of rain."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 559,
"part_index": null,
"start_index": 477,
"text": "* **Monday:** Mostly cloudy during the day with a chance of light rain at night."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 610,
"part_index": null,
"start_index": 560,
"text": "High near 68°F (20°C), low around 63°F (17°C)."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
0
],
"segment": {
"end_index": 633,
"part_index": null,
"start_index": 611,
"text": "20-25% chance of rain."
}
},
{
"confidence_scores": null,
"grounding_chunk_indices": [
1
],
"segment": {
"end_index": 698,
"part_index": null,
"start_index": 635,
"text": "Some forecasts indicate a chance of showers arriving next week."
}
}
],
"retrieval_metadata": {
"google_search_dynamic_retrieval_score": null
},
"retrieval_queries": null,
"search_entry_point": {
"rendered_content": "<style>\n.container {\n align-items: center;\n border-radius: 8px;\n display: flex;\n font-family: Google Sans, Roboto, sans-serif;\n font-size: 14px;\n line-height: 20px;\n padding: 8px 12px;\n}\n.chip {\n display: inline-block;\n border: solid 1px;\n border-radius: 16px;\n min-width: 14px;\n padding: 5px 16px;\n text-align: center;\n user-select: none;\n margin: 0 8px;\n -webkit-tap-highlight-color: transparent;\n}\n.carousel {\n overflow: auto;\n scrollbar-width: none;\n white-space: nowrap;\n margin-right: -12px;\n}\n.headline {\n display: flex;\n margin-right: 4px;\n}\n.gradient-container {\n position: relative;\n}\n.gradient {\n position: absolute;\n transform: translate(3px, -9px);\n height: 36px;\n width: 9px;\n}\n@media (prefers-color-scheme: light) {\n .container {\n background-color: #fafafa;\n box-shadow: 0 0 0 1px #0000000f;\n }\n .headline-label {\n color: #1f1f1f;\n }\n .chip {\n background-color: #ffffff;\n border-color: #d2d2d2;\n color: #5e5e5e;\n text-decoration: none;\n }\n .chip:hover {\n background-color: #f2f2f2;\n }\n .chip:focus {\n background-color: #f2f2f2;\n }\n .chip:active {\n background-color: #d8d8d8;\n border-color: #b6b6b6;\n }\n .logo-dark {\n display: none;\n }\n .gradient {\n background: linear-gradient(90deg, #fafafa 15%, #fafafa00 100%);\n }\n}\n@media (prefers-color-scheme: dark) {\n .container {\n background-color: #1f1f1f;\n box-shadow: 0 0 0 1px #ffffff26;\n }\n .headline-label {\n color: #fff;\n }\n .chip {\n background-color: #2c2c2c;\n border-color: #3c4043;\n color: #fff;\n text-decoration: none;\n }\n .chip:hover {\n background-color: #353536;\n }\n .chip:focus {\n background-color: #353536;\n }\n .chip:active {\n background-color: #464849;\n border-color: #53575b;\n }\n .logo-light {\n display: none;\n }\n .gradient {\n background: linear-gradient(90deg, #1f1f1f 15%, #1f1f1f00 100%);\n }\n}\n</style>\n<div class=\"container\">\n <div class=\"headline\">\n <svg class=\"logo-light\" width=\"18\" height=\"18\" viewBox=\"9 9 35 35\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M42.8622 27.0064C42.8622 25.7839 42.7525 24.6084 42.5487 23.4799H26.3109V30.1568H35.5897C35.1821 32.3041 33.9596 34.1222 32.1258 35.3448V39.6864H37.7213C40.9814 36.677 42.8622 32.2571 42.8622 27.0064V27.0064Z\" fill=\"#4285F4\"/>\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M26.3109 43.8555C30.9659 43.8555 34.8687 42.3195 37.7213 39.6863L32.1258 35.3447C30.5898 36.3792 28.6306 37.0061 26.3109 37.0061C21.8282 37.0061 18.0195 33.9811 16.6559 29.906H10.9194V34.3573C13.7563 39.9841 19.5712 43.8555 26.3109 43.8555V43.8555Z\" fill=\"#34A853\"/>\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M16.6559 29.8904C16.3111 28.8559 16.1074 27.7588 16.1074 26.6146C16.1074 25.4704 16.3111 24.3733 16.6559 23.3388V18.8875H10.9194C9.74388 21.2072 9.06992 23.8247 9.06992 26.6146C9.06992 29.4045 9.74388 32.022 10.9194 34.3417L15.3864 30.8621L16.6559 29.8904V29.8904Z\" fill=\"#FBBC05\"/>\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M26.3109 16.2386C28.85 16.2386 31.107 17.1164 32.9095 18.8091L37.8466 13.8719C34.853 11.082 30.9659 9.3736 26.3109 9.3736C19.5712 9.3736 13.7563 13.245 10.9194 18.8875L16.6559 23.3388C18.0195 19.2636 21.8282 16.2386 26.3109 16.2386V16.2386Z\" fill=\"#EA4335\"/>\n </svg>\n <svg class=\"logo-dark\" width=\"18\" height=\"18\" viewBox=\"0 0 48 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"24\" cy=\"23\" fill=\"#FFF\" r=\"22\"/>\n <path d=\"M33.76 34.26c2.75-2.56 4.49-6.37 4.49-11.26 0-.89-.08-1.84-.29-3H24.01v5.99h8.03c-.4 2.02-1.5 3.56-3.07 4.56v.75l3.91 2.97h.88z\" fill=\"#4285F4\"/>\n <path d=\"M15.58 25.77A8.845 8.845 0 0 0 24 31.86c1.92 0 3.62-.46 4.97-1.31l4.79 3.71C31.14 36.7 27.65 38 24 38c-5.93 0-11.01-3.4-13.45-8.36l.17-1.01 4.06-2.85h.8z\" fill=\"#34A853\"/>\n <path d=\"M15.59 20.21a8.864 8.864 0 0 0 0 5.58l-5.03 3.86c-.98-2-1.53-4.25-1.53-6.64 0-2.39.55-4.64 1.53-6.64l1-.22 3.81 2.98.22 1.08z\" fill=\"#FBBC05\"/>\n <path d=\"M24 14.14c2.11 0 4.02.75 5.52 1.98l4.36-4.36C31.22 9.43 27.81 8 24 8c-5.93 0-11.01 3.4-13.45 8.36l5.03 3.85A8.86 8.86 0 0 1 24 14.14z\" fill=\"#EA4335\"/>\n </svg>\n <div class=\"gradient-container\"><div class=\"gradient\"></div></div>\n </div>\n <div class=\"carousel\">\n <a class=\"chip\" href=\"https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE4xbVxpim7Nu37sZkcoIiPX6loDIe3Iw6bz4n_L54s89cP7SEe0lZXdzCaMinyCkxwnuwISe5nc_Qp0m660yoFkV_j9JspBIS8Vmuj9Nwmn02jETDrWYf-pSz9a2_0qulSZiuxS_MEl3Me19fWPjR7oyPak2OvkGhSHrffAHo03ai0b0EMT7LoohIFV33ugJIwBJ6mPpIE49BGRQ==\">weather in San Francisco</a>\n </div>\n</div>\n",
"sdk_blob": null
},
"web_search_queries": [
"weather in San Francisco"
]
},
"index": null,
"logprobs_result": null,
"safety_ratings": null
}
],
"create_time": "2025-09-07T01:50:42.111555Z",
"model_version": "gemini-2.5-flash-lite",
"prompt_feedback": null,
"response_id": "8uS8aMPnBq6T998P1oezwA8",
"usage_metadata": {
"cache_tokens_details": null,
"cached_content_token_count": null,
"candidates_token_count": 222,
"candidates_tokens_details": [
{
"modality": "TEXT",
"token_count": 222
}
],
"prompt_token_count": 8,
"prompt_tokens_details": [
{
"modality": "TEXT",
"token_count": 8
}
],
"thoughts_token_count": null,
"tool_use_prompt_token_count": null,
"tool_use_prompt_tokens_details": null,
"total_token_count": 230,
"traffic_type": "ON_DEMAND"
},
"automatic_function_calling_history": [],
"parsed": null
}
Let's break this down:
web_search_queries
: Shows the exact query the model used:"weather in San Francisco"
.grounding_chunks
: Lists the web pages the model used as sources to formulate its answer. In this case, it used a main Google Search result and a page fromktvu.com
.grounding_supports
: This is the most powerful part. It directly links segments of the generated text back to the sources ingrounding_chunks
. For example, it shows that the sentence about the current temperature came from source0
(google.com), and the sentence about next week's showers came from source1
(ktvu.com).
This metadata provides verifiability and trust. You're not just getting a black-box answer; you can see exactly where the information came from, allowing you to build more reliable and transparent AI applications. 🔍
⚠️ Keep in mind, Google has detailed terms of service for using Grounding with Google Search. Please refer to the Google Documentation before adding Grounding with Google Search to any application.
Conclusion
Grounding is a simple yet transformative feature that bridges the gap between an LLM's static knowledge and the dynamic, real-time world. By adding a single tool to your SDK call, you can empower Gemini to answer a whole new class of questions with up-to-date, verifiable information from Google Search. Happy building!