Google reverse geocoding: Get addresses from coordinates
Learn how to use the Google Geocoding API for reverse geocoding - convert GPS coordinates into human readable addresses.

Reverse geocoding is the process of converting geographic coordinates (latitude and longitude) into a human readable address. In this blog post, I’ll show you how to use the Google Geocoding API to reverse geocode latitude and longitude coordinates into a clean, formatted address and display it on a map.

Part 1: A practical guide to the Google Geocoding API
Part 2: Forward geocoding with the Google Geocoding API
Part 3: Google reverse geocoding: Get addresses from coordinates (this article)
Part 4: Google Address Validation API: Better than USPS?
Part 5: Google Maps Building Outlines and Entrances API
As with our other tutorials, we'll use what we've learned to build a simple demo app (https://google-reverse-geocoding.afi.dev/). Code (frontend / backend) for this project is available for free on GitHub.

What is reverse geocoding?
Reverse geocoding turns any latitude and longitude pair into a structured, human readable address or place name, known as a reverse geocode. It’s the inverse of forward geocoding, which transforms addresses into geographic coordinates.

When you send a pair of coordinates to the reverse geocoding endpoint of the Google Geocoding API, Google matches them to the closest known feature in its geospatial database and returns a complete, display-ready address string. It's a technology thats widely used on social media apps for geotagging photos, recommending content and grouping posts based on neighborhoods, cities, or landmarks.

Google Geocoding API example (reverse geocoding)
Here's a simple geocoding example that returns an address string for 49.2846966, -123.1119349 in downtown Vancouver.
Endpoint: GET
https://maps.googleapis.com/maps/api/geocode/json?
latlng={LATLNG}&
key={YOUR_API_KEY}
{LATLNG} is the comma separated latitude and longitude coordinates of the location you want to convert into the nearest human readable address.
{YOUR_API_KEY} is your Google Maps API key with the Google Geocoding API enabled.
Endpoint: GET
https://maps.googleapis.com/maps/api/geocode/json?latlng=49.2846966,-123.1119349&key={YOUR_API_KEY}
Response
{
"plus_code": {
"compound_code": "7VMQ+V6G Vancouver, BC, Canada",
"global_code": "84XR7VMQ+V6G"
},
"results": [
{
"address_components": [
//... address components array
],
"formatted_address": "555 W Hastings St, Vancouver, BC V6B 4N4, Canada",
"geometry": {
"bounds": {
"northeast": {
"lat": 49.2851751,
"lng": -123.111479
},
"southwest": {
"lat": 49.28441489999999,
"lng": -123.1126507
}
},
"location": {
"lat": 49.2844998,
"lng": -123.1128788
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 49.28614398029149,
"lng": -123.1107158697085
},
"southwest": {
"lat": 49.28344601970849,
"lng": -123.1134138302915
}
}
},
"navigation_points": [
{
"location": {
"latitude": 49.2845048,
"longitude": -123.1124829
}
}
],
"place_id": "ChIJUcKFZ3hxhlQREJGVU1foPaE",
"types": [
"premise",
"street_address"
]
},
//... 14 more entries
],
"status": "OK"
}
Just like with forward geocoding, the response to the reverse geocoding endpoint of the Google Geocoding API is an array, results
. Unlike forward geocoding though, reverse geocoding typically returns multiple results, ranked from most precise to least precise - starting with the exact street address and followed by broader regions like neighborhoods, cities, counties, and states. In the response above, 15 locations were returned:
street_address
1200-555 W Hastings St, Vancouver, BC V6B 4N6, Canadastreet_address
premise
555 W Hastings St, Vancouver, BC V6B 4N4, Canada
...
city
political
Vancouver, BC, Canadaadministrative_area_level_2
political
Metro Vancouver, BC, Canadaadministrative_area_level_1
political
British Columbia, Canadacountry
political
Canada
From the list above, you can see that the geocoded point may be denoted as a street_address
, a city
(Vancouver), a province administrative_level_1
(British Columbia) or as a country
(Canada). All are "addresses" to the geocoder. Which one you use depends on your needs. For deliveries or field services, you'll want exact street level accuracy. But for tasks like geospatial analytics, a more general "address" - like a postal code or district, might be enough.

An interesting detail about reverse geocoding is that the returned address (reverse geocode) isn’t always located exactly at the input coordinates - it’s often the nearest known address instead. The easiest way to illustrate this is by reverse geocoding coordinates in the ocean (picture above) and notice how the returned address appears on the nearest shoreline.
In each result
object:
The formatted_address
field contains the complete, human readable version of the address - exactly how you’d write it for mailing purposes, such as on a letter or postcard.
The types
field is an array that describes the categories associated with the geocoded result. Most addresses belong to multiple types. For instance, “Vancouver, Canada” is labeled as both locality
and political
, indicating that it functions as a city and also as a recognized political region.
The geometry
object contains geographic details about the geocoded result, specifically:
location
, which contains the exactlocation.lat
andlocation.lng
of the place,
{
"location": {
"lat": 49.2846966,
"lng": -123.1119349
}
}
location_type
, which indicates the accuracy of the geocoded result.- "ROOFTOP" means the result is highly precise, accurate down to a specific street address.
- "RANGE_INTERPOLATED" and "APPROXIMATE" suggest the location is estimated and less reliable.
The place_id
(docs) is a unique identifier assigned by Google Maps to a specific place. You can use this ID to look up more detailed information about the location, such as photos, user ratings, and reviews.
Filtering reverse geocoding results by type
Because the reverse geocoding endpoint returns multiple addresses - ranging from a precise street address to broader areas like neighborhoods, cities, and states, it's important to filter the results by precision to fit your use case.
For example, if you are using reverse geocoding to show where a delivery occurred, you would want to use a result with the "street_address" type
. If you are using it to tag photos, broader types
like "neighborhood" or "locality" might be more appropriate.
To filter by type, add the result_type
parameter to the Google Geocoding API call like so:
https://maps.googleapis.com/maps/api/geocode/json?latlng=49.2846966,-123.1119349&key={YOUR_API_KEY}&result_type=street_address
Including result_type=street_address
in your request filters the response to only include results of the type
"street_address". Note that result_type
doesn't limit the search itself - instead, it acts as a post-processing filter. The API retrieves all possible results for the given coordinates, then removes any that don’t match the specified type
.
Building a Google Maps reverse geocoding demo app
In this final section, we’ll build a web app (https://google-reverse-geocoding.afi.dev/) using the reverse geocoding endpoint of the Google Geocoding API to better understand how reverse geocoding works. Users can drag and drop a marker anywhere on a Google Map to retrieve the formatted_address
of the nearest reverse geocode. A blue circle icon will indicate the location of that address. This app highlights how the input coordinates and the returned reverse geocode location can sometimes differ - often significantly.
How our code is structured
google_reverse_geocoding is a single page React app that uses Vite as the build tool and development server. The app works together with google_geocoding_backend, which retrieves reverse geocode data from the Google Geocoding API. As with our other tutorials, we'll be using the super useful Google Maps React component library from @vis.gl/react-google-maps to quickly scaffold our app and manage map interactions and UI in a way that is consistent with React. The <Map/>
, <DraggableMarker/>
and <LocationMarker/>
components are examples of this.
App.jsx
App.jsx
is the main entry point of the app. It manages the overall layout, routing, structure, and handles the call to the backend to perform reverse geocoding using the Google Geocoding API.

App.jsx
has four main components:
<SearchBox/>
, which holds the Place Autocomplete text box (<PlaceAutocompleteInput/>
) that is used to switch the map view to any location worldwide.<GoogleMap/>
, a React wrapper around the 2D Maps JavaScript API provided by the @vis.gl/react-google-maps library.<DraggableMarker/>
and<LocationMarker/>
, the marker components for the draggable red balloon pin and its corresponding reverse geocoded location (displayed on the map as a blue dot), and<InfoWindow/>
, a temporary overlay that shows up when<DraggableMarker/>
is clicked. This pop up shows theformatted_address
of the reverse geocoded pin location, together with the actual latitude and longitude of the pin marker.
/*** App.jsx ***/
import { APIProvider } from "@vis.gl/react-google-maps";
import { useState, useEffect } from "react";
import axios from "axios";
import GoogleMap from "~/components/GoogleMap";
import SearchBox from "~/components/SearchBox";
import "./App.scss";
function App() {
const [autocompletePlace, setAutocompletePlace] = useState(null);
const [reversePlace, setReversePlace] = useState(null);
const handleSearchPlace = async ({ lat, lng }) => {
const res = await axios.get(
`${import.meta.env.VITE_API_URL}/reverse-geocode`,
{
params: { lat, lng },
},
);
if (res.data && res.data.results) {
const nearestPlace = res.data.results?.find((place) => !place.plus_code);
setReversePlace(nearestPlace);
}
};
const handleDragEnd = (location) => {
handleSearchPlace(location);
};
// Reset reverse place
useEffect(() => {
if (autocompletePlace) {
setReversePlace(autocompletePlace);
}
}, [autocompletePlace]);
return (
<APIProvider apiKey={import.meta.env.VITE_GOOGLE_API_KEY}>
<div className="App">
<SearchBox onSelectAutocompletePlace={setAutocompletePlace} />
<GoogleMap
autocompletePlace={autocompletePlace}
reversePlace={reversePlace}
onDragEnd={handleDragEnd}
/>
</div>
</APIProvider>
);
}
export default App;
SearchBox.jsx
SearchBox.jsx
is a basic presentational component that contains a text input field, implemented using the <PlaceAutoCompleteInput/>
child component. The input is powered by Google Place Autocomplete, which allows users to quickly find and select addresses or place names.
/*** components/SearchBox.jsx ***
import { useMap, useMapsLibrary } from "@vis.gl/react-google-maps";
import { useCallback, useEffect, useState } from "react";
import Icon from "~/components/Icon";
import PlaceAutocompleteInput from "~/components/PlaceAutocompleteInput";
import "./SearchBox.scss";
const SearchBox = ({ onSelectAutocompletePlace }) => {
const map = useMap();
const googleMaps = useMapsLibrary("places");
const [placeService, setPlaceService] = useState(null);
useEffect(() => {
if (googleMaps && map) {
setPlaceService(new googleMaps.PlacesService(map));
}
}, [map, googleMaps]);
const handlePlaceSelect = useCallback(
(autocompletePlace) => {
onSelectAutocompletePlace(autocompletePlace);
},
[placeService],
);
return (
<div className="SearchBox">
<label className="label">Location</label>
<div className="input-group">
<PlaceAutocompleteInput onPlaceSelect={handlePlaceSelect} />
<Icon type="search" size={24} />
</div>
</div>
);
};
export default SearchBox;
PlaceAutocompleteInput.jsx
The <PlaceAutocompleteInput/>
component, as the name suggests, contains a text input field that uses Google Places Autocomplete to suggest places dynamically as the user types. When the user selects a place from the dropdown menu, Google triggers a place_changed
event on the Autocomplete instance.
PlaceAutocompleteInput.jsx
listens for this event and uses getPlace()
to retrieve the selected place's details (place_id
, geometry
, name
, and formatted_address
), and saves it to an autocompletePlace
object. This data is passed on to its parent component via the onPlaceSelect()
prop and eventually passed to App.jsx
where it is saved in state.
/*** components/PlaceAutocomepleteInput.jsx ***
import { useMapsLibrary } from "@vis.gl/react-google-maps";
import { useEffect, useRef, useState } from "react";
const PlaceAutocompleteInput = ({ onPlaceSelect }) => {
const [placeAutocomplete, setPlaceAutocomplete] = useState(null);
const inputRef = useRef(null);
const places = useMapsLibrary("places");
useEffect(() => {
if (!places || !inputRef.current) return;
const options = {
fields: ["place_id", "geometry", "name", "formatted_address"],
};
setPlaceAutocomplete(new places.Autocomplete(inputRef.current, options));
}, [places]);
useEffect(() => {
if (!placeAutocomplete) return;
placeAutocomplete.addListener("place_changed", () => {
onPlaceSelect(placeAutocomplete.getPlace());
});
}, [onPlaceSelect, placeAutocomplete]);
return (
<div className="PlaceAutocompleteInput">
<input ref={inputRef} />
</div>
);
};
export default PlaceAutocompleteInput;
GoogleMap.jsx
GoogleMap.jsx
, found in components/GoogleMap/index.jsx
, is a React component that displays a full screen Google Map using the @vis.gl/react-google-maps library. It handles three main tasks:
- Centers the map at a new location whenever
autocompletePlace
is updated. - Displays a draggable red pin marker and a blue circle icon at the location of
reversePlace
. - Calls the
handleDragEnd()
function (passed through theonDragEnd
prop) when the user finishes dragging the red pin marker.
/*** components/GoogleMap/index.jsx ***
import { Map } from "@vis.gl/react-google-maps";
import MapHandler from "./MapHandler";
import LocationMarker from "./LocationMarker";
import DraggableMarker from "./DraggableMarker";
const DEFAULT_CENTER = { lat: 49.25307278849622, lng: -123.12095840000302 };
const GoogleMap = ({ autocompletePlace, reversePlace, onDragEnd }) => {
return (
<div className="GoogleMap">
<Map
style={{
height: "100dvh",
width: "100dvw",
}}
defaultZoom={12}
mapId={import.meta.env.VITE_GOOGLE_MAP_ID}
defaultCenter={DEFAULT_CENTER}
gestureHandling="greedy"
disableDefaultUI
reuseMaps
>
<MapHandler place={autocompletePlace} />
{reversePlace && (
<LocationMarker key={reversePlace.place_id} place={reversePlace} />
)}
{autocompletePlace && (
<DraggableMarker
place={autocompletePlace}
reversePlace={reversePlace}
onDragEnd={onDragEnd}
/>
)}
</Map>
</div>
);
};
export default GoogleMap;
When autocompletePlace
is not null
(meaning the user has selected a location from the address autocomplete input), the red pin <DraggableMarker/>
is displayed on the map. When a reverse geocoded location is available (i.e., reversePlace
is not null
), the blue circle icon from <LocationMarker/>
is also shown.
DraggableMarker.jsx

<DraggableMarker/>
uses the <AdvancedMarker/>
component from the @vis.gl/react-google-maps library as a wrapper for Google's AdvancedMarkerElement. It works seamlessly with React state, and re-renders intelligently when props like position
, zIndex
, or children
change.
/*** components/GoogleMap/DraggableMarker.jsx ***/
<AdvancedMarker
ref={markerRef}
draggable={true}
zIndex={2}
position={position}
onDragEnd={handleDragEnd}
onClick={() => setInfoOpen((prev) => !prev)}
>
<Pin />
</AdvancedMarker>;
In the return statement of DraggableMarker.jsx
, we render an <AdvancedMarker/>
with draggable={true}
. This allows users to click and drag the marker on the map just as if you had set marker.setDraggable(true)
in plain Javascript. Google Maps also automatically adds an “X” icon at the base of the marker on hover to indicate that the marker can be dragged, which is the default behavior from the Google Maps Javascript API’s Web Components library.
When the user finishes dragging the marker - that is, when they drop it anywhere on the map - the onDragEnd
event fires and handleDragEnd()
is called. This function receives e
, the event object from Google Maps which contains the marker’s new position. The state of <DraggableMarker/>
is then updated with this new location using setPosition(location)
, which sets it in place.
/*** components/GoogleMap/DraggableMarker.jsx ***/
const handleDragEnd = (e) => {
const location = { lat: e.latLng.lat(), lng: e.latLng.lng() };
setPosition(location);
onDragEnd(location);
};
Finally, the location object is passed to onDragEnd()
, a callback function provided via props from App.jsx
. In App.jsx
, onDragEnd
points to handleDragEnd
which in turn calls the handleSearchPlace()
method, which is responsible for implementing an API loading wrapper design pattern to reverse geocode the coordinates in location
.
/*** App.jsx ***/
const handleSearchPlace = async ({ lat, lng }) => {
const res = await axios.get(
`${import.meta.env.VITE_API_URL}/reverse-geocode`,
{
params: { lat, lng },
},
);
if (res.data && res.data.results) {
const nearestPlace = res.data.results?.find((place) => !place.plus_code);
setReversePlace(nearestPlace);
}
};
const handleDragEnd = (location) => {
handleSearchPlace(location);
};
After receiving a response from the /reverse-geocode
endpoint of google_geocoding_backend, we loop through the results and select the first one that doesn’t include a plus_code
property with const nearestPlace = res.data.results?.find((place) => !place.plus_code);
. Since Plus Codes are used for locations without traditional street addresses - and we’re looking for actual addresses - we skip those. Because reverse geocoding results are sorted by precision, this ensures we get the nearest and most relevant address for our marker’s location. The result is used to update the reversePlace
state with the value of nearestPlace
.
nearestPlace
is the closest human readable address to our marker coordinates. It's a Geocoding Response Object with the following structure (we've seen it before in our earlier reverse geocoding example):
{
"address_components": [
//... address components array
],
"formatted_address": "555 W Hastings St, Vancouver, BC V6G 3J4, Canada",
"geometry": {
"location": {
"lat": 49.2844998,
"lng": -123.1128788
},
"location_type": "ROOFTOP",
// ... javascript object
},
"navigation_points": [
//... javascript object
],
"place_id": "ChIJw6AQfXhxhlQRSgv78ppPKXc",
"types": [
"street_address",
"subpremise"
]
}
The formatted_address
is used as the heading of the <InfoWindow/>
component while its geographic coordinates (geometry.location.lat
and geometry.location.lng
) are used to position the blue circle <LocationMarker/>
. In the example above, the reverse geocoded nearestPlace
is actually across the street from the location of the draggable marker.

LocationMarker.jsx
Like <DraggableMarker />
, <LocationMarker />
is a wrapper around Google Map's AdvancedMarkerElement class. However, instead of using the default red balloon pin like <DraggableMarker />
, <LocationMarker/>
displays a custom SVG icon styled as a blue circle.
/*** components/GoogleMap/LocationMarker.jsx ***/
import { AdvancedMarker, Pin } from "@vis.gl/react-google-maps";
import { useEffect, useState } from "react";
import Icon from "~/components/Icon";
import { getLatLng } from "~/utils";
import "./Marker.scss";
const LocationMarker = ({ place }) => {
const [position, setPosition] = useState(() =>
getLatLng(place.geometry.location),
);
return (
<AdvancedMarker
draggable={false}
zIndex={1}
position={getLatLng(place.geometry.location)}
>
<Icon type="marker" />
</AdvancedMarker>
);
};
export default LocationMarker;
Here's a detailed explanation of how this works:
- The
<Icon/>
component returns an SVG element, and when nested inside an<AdvancedMarker/>
element from @vis.gl/react-google-maps, React allows you to insert that SVG directly in to the map as a custom marker with your own design and styling. <Icon/>
is imported from/components/Icon.jsx
, which returns an<ImageSVG/>
taken from/assets/index.js
.<Icon/>
was given atype
prop of "marker". InIcon.jsx
, the lineconst ImageSVG = icons[type]
means that whentype
is "marker",<ImageSVG/>
gets its data fromicons["marker"]
, which in/assets/index.js
points toassets/marker.svg
.marker.svg
was added to our app with the lineimport marker from '~/assets/marker.svg?react'
. The?react
appended at the end tells the build system to load the SVG as a React component, instead of a URL or an image file.- When rendered as an SVG, the marker is styled as a blue circle with a white border (below).

When the red <DraggableMarker/>
is moved to a new position, the app calls the reverse geocoding endpoint of the Google Geocoding API again, and updates reversePlace
with the new location.
See reverse geocoding in action
To see reverse geocoding at work, head over to https://google-reverse-geocoding.afi.dev/ and enter an address or place name e.g. "Vancouver, BC" in the text field at the top left. Drag the marker around the map to see the reverse geocoded address and location details update in real time.
If you prefer to run the app locally:
- Fork both google_reverse_geocoding and google_geocoding_backend repositories on GitHub.
- In google_geocoding_backend, run
npm install
to install the dependencies followed bynpm start
. - In google_reverse_geocoding, run
npm install
followed byvite run dev
to start the app. Open your browser to http://localhost:3000/ to view the app.
Remember to update the .env
files in both folders with a Google Maps API key that has the Google Geocoding API enabled.
Coming up
In the next section, we'll touch on the Google Address Validation API, which is used to validate, correct, and standardize postal addresses according to official data sources. Unlike the Google Geocoding API, which tells you where an address is (or in reverse geocoding, what approximate address matches a set of coordinates), the Address Validation API checks whether an address is real, complete, and deliverable - in other words, if you mailed a letter to that address, it would actually arrive.
Next: Part 4: Google Address Validation API: Better than USPS?