●CLI — Gemini CLI and the Gemini Code Assist IDE extensions stopped serving requests on Jun 18; migrate to Antigravity or the new Go-based Antigravity CLI●FLASH — Gemini 3.5 Flash is now generally available, billed as the smartest model for sustained frontier performance on agentic and coding tasks●IMAGE — gemini-3.1-flash-image-preview and gemini-3-pro-image-preview are deprecated and shut down on Jun 25; move to the successor models●AGENTS — Managed Agents is in public preview, running stateful autonomous agents in secure, isolated Google-hosted Linux sandboxes●SEARCH — File Search now supports multimodal image search natively via the gemini-embedding-2 model●MIGRATE — With deadline-bound deprecations piling up, any automation built on the CLI or old models needs a tracked migration●CLI — Gemini CLI and the Gemini Code Assist IDE extensions stopped serving requests on Jun 18; migrate to Antigravity or the new Go-based Antigravity CLI●FLASH — Gemini 3.5 Flash is now generally available, billed as the smartest model for sustained frontier performance on agentic and coding tasks●IMAGE — gemini-3.1-flash-image-preview and gemini-3-pro-image-preview are deprecated and shut down on Jun 25; move to the successor models●AGENTS — Managed Agents is in public preview, running stateful autonomous agents in secure, isolated Google-hosted Linux sandboxes●SEARCH — File Search now supports multimodal image search natively via the gemini-embedding-2 model●MIGRATE — With deadline-bound deprecations piling up, any automation built on the CLI or old models needs a tracked migration
Building location-aware AI with Gemini's Google Maps grounding: pricing and the source-display rules tutorials skip
How to ship a 'recommend something nearby' feature with Gemini API's Google Maps grounding, with the $25/1K cost design and the source-display obligations laid out for indie developers.
I once wanted to add a small feature to one of my calming apps: suggest a quiet café nearby where a user could step away for a moment. Most of the apps I run as an indie developer have never touched location data, so the first thing that hit me was a practical wall — how would I ever maintain a database of places worldwide? Calling a paid Places API and building my own ranking on top is too heavy to run alone.
Gemini API's Google Maps grounding is what lets you go around that wall. It hands the model a "sense of place" and backs answers with real Google Maps data (businesses, reviews, opening hours). As of June 2026 it works on several models, including Gemini 3.5 Flash. Getting it to respond takes only a few lines — but if you ship it without understanding the billing structure and the attribution rules, it bites back later. Here are the things I sorted out while wiring it up.
Grounding hands the model a "sense of place"
Under the hood, this tool is a text search. When a user's query carries geographical context ("near me," "in San Francisco"), the model queries Google Maps and generates an answer informed by the result. If you pass latitude and longitude, "near me" style queries are interpreted around those coordinates, while specific or non-local queries are barely influenced by them.
The flow goes like this: the user sends a query with geographical intent, the model invokes the tool, the Maps service pulls places and reviews, that real data shapes the answer, and a text response comes back with sources attached. If you think of it as the "retrieve → inject context → generate" of a hand-built RAG pipeline, but specialized for place data and handled on Google's side, it clicks into place.
One thing worth noting: the tool is off by default. It only runs on requests where you explicitly enable it. That is not an oversight — given the billing and latency below, it is a welcome default.
Minimal implementation — enable the tool and pass coordinates
Start plainly. Put Google Maps into tools, and optionally pass coordinates through toolConfig. Here it is in the official Python SDK (google-genai).
from google import genaifrom google.genai import typesclient = genai.Client() # reads GEMINI_API_KEY from the environmentprompt = "Is there a quiet café within a 15-minute walk from here?"response = client.models.generate_content( model="gemini-3.5-flash", contents=prompt, config=types.GenerateContentConfig( # Turn on Google Maps grounding tools=[types.Tool(google_maps=types.GoogleMaps())], # Optional: pass the user's location as context (this is near Tokyo Station) tool_config=types.ToolConfig( retrieval_config=types.RetrievalConfig( lat_lng=types.LatLng(latitude=35.681236, longitude=139.767125) ) ), ),)print(response.text)
JavaScript (@google/genai) has the same shape: put googleMaps: {} in tools, and pass coordinates to toolConfig.retrievalConfig.latLng.
import { GoogleGenAI } from "@google/genai";const ai = new GoogleGenAI({}); // GEMINI_API_KEY from the environmentconst response = await ai.models.generateContent({ model: "gemini-3.5-flash", contents: "Is there a quiet café within a 15-minute walk from here?", config: { tools: [{ googleMaps: {} }], toolConfig: { retrievalConfig: { latLng: { latitude: 35.681236, longitude: 139.767125 }, }, }, },});console.log(response.text);
Ask for "cafés nearby" and you will get a plausible answer. But putting that on screen as-is violates the terms, because you must display the returned sources according to the rules.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Get a minimal 'recommend something nearby' implementation running on Gemini 3.5 Flash, in both Python and JavaScript, without owning a place database
✦Understand why $25 / 1K separate-line billing makes 'always on' unsustainable, and switch to a design that enables the tool only when the query is geographical
✦Meet the groundingChunks source-display obligations (Google Maps attribution rules) and avoid the policy issues that can get your app cut off
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Displaying sources is an obligation, not an option
When an answer is backed by Maps data, the response carries groundingMetadata. It has two main parts: groundingChunks, an array of sources (uri, title, placeId), and groundingSupports, an array that links "which span of the body text maps to which source" using startIndex, endIndex, and chunk indices. The latter is what lets you build inline citations for specific sentences.
The official usage requirements state that when you show a Maps-grounded result, you must present the corresponding Google Maps sources "immediately after that content" and "viewable within one user interaction." The attribution text itself has rules too: you must not alter the string Google Maps. Don't change its capitalization, don't wrap it onto multiple lines, don't localize it into another language, and add translate="no" to stop browsers from auto-translating it.
A minimal source list can be assembled like this.
function renderSources(response) { const grounding = response.candidates?.[0]?.groundingMetadata; const chunks = grounding?.groundingChunks ?? []; // No sources means the Maps grounding did not take effect if (chunks.length === 0) return null; const items = chunks .filter((c) => c.maps) // keep only maps sources .map((c) => ({ title: c.maps.title, uri: c.maps.uri, // a reference link to maps.google.com placeId: c.maps.placeId, })); return items; // render this array right after the body, with the "Google Maps" label}
On the HTML side, always include an unaltered attribution line.
<p class="gmp-attribution" translate="no">Google Maps</p><ul> <!-- list the renderSources() output as links here --></ul>
placeId and reviewId are allowed to be cached, stored, and exported, so you can tie a place to your own database and reuse it. What you cannot do is skip the source display. This dovetails with the billing rule below — "a response with zero sources means the Maps grounding did not take effect, so it is not billed" — which makes the presence of groundingChunks the trigger for both display and billing logic.
Pricing is a separate line — why "always on" breaks
This is the crux for an indie developer. Google Maps grounding is billed separately from model input/output tokens: $25 per 1,000 successfully grounded prompts (as of June 2026). The free tier allows up to 500 requests per day. Only prompts that return a result containing at least one Maps source count, and multiple Maps lookups inside a single request still count as one.
Put numbers on it and the weight shows. An app with 100 geographical queries a day runs about 3,000 a month, roughly $75. Push that to 500 a day and you are at 15,000 a month, about $375. For a free app running thin on AdMob revenue, attaching that unit cost to every request collapses the math quickly. For comparison, here is the difference in how the two are charged.
Item
Billing unit
Free tier
Maps grounding
$25 per 1,000 successfully grounded prompts
500 requests/day
Model inference
Metered by input/output token volume
Follows the model's rate limits
The official best practices say it outright: turn it off when you don't need it. Being off by default is a sound call given that unit price. So the real focus of the implementation shifts from "how to enable it" to "which requests to keep it off for."
Enable the tool only when the intent is geographical
The approach I took is to not leave the tool always on, and instead place one gate in front that enables it only when the query likely has geographical intent. The judgment itself doesn't need to be precise — a light preprocessing step is enough.
import reGEO_HINTS = ["near", "nearby", "around here", "from here", "walk", "station", "café", "cafe", "restaurant"]def looks_geographical(text: str) -> bool: # just a light check for place / distance / category words lowered = text.lower() return any(hint in lowered for hint in GEO_HINTS)def build_config(prompt: str, lat: float | None, lng: float | None): if not looks_geographical(prompt): # no geographical intent -> don't attach the tool -> not billable return types.GenerateContentConfig() tool_config = None if lat is not None and lng is not None: tool_config = types.ToolConfig( retrieval_config=types.RetrievalConfig( lat_lng=types.LatLng(latitude=lat, longitude=lng) ) ) return types.GenerateContentConfig( tools=[types.Tool(google_maps=types.GoogleMaps())], tool_config=tool_config, )
This single gate keeps non-geographical requests — small talk, how-to-use-the-app questions — from flowing to Maps, and makes your monthly bill predictable. I recommend erring toward catching geographical intent too eagerly. If a miss leaves the tool off, the worst case is "an ordinary answer that isn't place-aware," a small degradation. And if you over-catch, the "zero sources means no charge" rule has your back. Note that on the Gemini 3 family you can combine Maps grounding with function calling (your own tools), so extending the design with functions for coordinate lookup or booking flows is straightforward.
Pitfalls an indie developer actually hits
Here are the points I stumbled on, or read in the requirements and thought "this will cause an incident."
First, throwing a "near me" query while forgetting to pass coordinates. Without latitude and longitude, the model has no reference point and won't give the proximity result you expected. When the client's location permission isn't granted, even if you know the intent is geographical, prompting "I need your current location" honestly beats returning an off-target answer.
Second, the territory problem. Distributing or marketing an app that uses Maps grounding in a prohibited territory is restricted by the terms. The more widely you ship a multilingual indie app, the earlier you want to confirm your distribution regions.
Third, input and output are text only. Maps grounding currently does not support multimodal input or output. A use case like identifying a store from a photo needs to be combined with a different mechanism.
Fourth, latency. If you embed it into a conversational UI, the guidance is to measure the P95 of grounded responses and watch that it stays within an acceptable range. Since a round trip to Maps is added, assume it is slower than a plain model response and prepare a loading state so the experience stays steady.
A next step
Start by writing down five queries with likely geographical intent in your own app, and check whether groundingChunks come back both with and without coordinates. Once you can see the shape of queries that return sources reliably, your gate keywords and the minimum requirements for the source-display UI settle at the same time. For me, deciding "what to show when zero sources come back" before writing any code kept both the billing and the display decisions from wobbling. I hope this serves as a first map for other indie developers weighing a location feature.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.