Address entry looks simple until users have to type the same address fields again and again.
For a canvas app, a better pattern is to let the user start typing a street address, show address suggestions, and then populate the final address fields from the selected suggestion.
This note uses Google Places API through a Power Platform custom connector. The app calls Place Autocomplete for suggestions, then calls Place Details after the user selects one address.
What We Are Building
The Power Apps experience has four parts:
- a text input where the user types the address
- a gallery that shows Google address suggestions
- selected address outputs, such as street, city, state, postal code, country, latitude, and longitude
- a debug JSON output so the raw response can be inspected while building
I prefer putting this into a canvas component so the same address picker can be reused across screens and apps.
The component should expose outputs such as:
SelectedAddressPlaceIdLatitudeLongitudeAddressJson
That keeps the screen clean. The screen should use the component as an address picker, not carry every connector detail itself.
Google Cloud Setup
In Google Cloud Console:
- Create a new project for the app or solution.
- Enable Places API for the project.
- Create an API key.
- Restrict the API key where possible.
- Confirm billing is configured for the project.
This post uses the legacy Places web service endpoints:
https://maps.googleapis.com/maps/api/place/autocomplete/json
https://maps.googleapis.com/maps/api/place/details/json
Google marks these legacy endpoints separately from Places API (New), so I would document that decision in the solution. If you later migrate to Places API (New), expect the connector shape and response parsing to change.
Why the Session Token Matters
Use one session token for the user's autocomplete session.
The token should start when the user begins an address search and should be reused for:
- all autocomplete calls while the user is typing
- the place details call after the user selects one result
After the details call, the session is complete. Generate a new token for the next search.
In Power Apps, a GUID works well:
Set(varAddressSessionToken, GUID());
The important part is not the exact token value. The important part is that autocomplete and details use the same token for one selection flow.
Create the Custom Connector
Create a new custom connector with these general settings:
Host: maps.googleapis.com
Base URL: /
Scheme: HTTPS
For security, use an API key:
Authentication type: API Key
Parameter label: API Key
Parameter name: key
Parameter location: Query
When the connection is created, Power Apps will ask for the API key.
Action 1: Get Address Predictions
Create the first action:
Summary: Get address predictions
Description: Returns Google Places address autocomplete suggestions.
Operation ID: GetAddressPredictions
Visibility: none
Import the request from a sample URL:
GET https://maps.googleapis.com/maps/api/place/autocomplete/json?input=kilo&types=address&components=country:so&sessiontoken=abc123
Use types=address, not type=address. Google Places Autocomplete expects the parameter name types.
Import this sample response:
{
"predictions": [
{
"description": "305 Cronin Boulevard, Shorewood, IL, USA",
"place_id": "abc123",
"terms": [
{ "offset": 0, "value": "305" },
{ "offset": 4, "value": "Cronin Boulevard" },
{ "offset": 22, "value": "Shorewood" }
],
"structured_formatting": {
"main_text": "305 Cronin Boulevard",
"secondary_text": "Shorewood, IL, USA"
},
"types": ["street_address"]
}
],
"status": "OK"
}
After import, confirm the connector exposes these query parameters:
inputtypescomponentssessiontokenkey
The key parameter should come from the connector connection, not from formulas in the app.
Action 2: Get Place Details
Create the second action:
Summary: Get place details
Description: Returns place details from a place ID.
Operation ID: GetPlaceDetails
Visibility: none
Import the request from a sample URL:
GET https://maps.googleapis.com/maps/api/place/details/json?place_id=abc123&fields=formatted_address,address_components,geometry&sessiontoken=abc123
Import this sample response:
{
"result": {
"formatted_address": "305 Cronin Boulevard, Shorewood, IL 60404, USA",
"address_components": [
{
"long_name": "60404",
"short_name": "60404",
"types": ["postal_code"]
}
],
"geometry": {
"location": {
"lat": 41.5201,
"lng": -88.2017
}
}
},
"status": "OK"
}
Confirm the connector exposes:
place_idfieldssessiontokenkey
The fields parameter is important. Without it, Place Details can return more data than the app needs.
Test the Connector
Before opening Power Apps, test both actions inside the custom connector.
For autocomplete, try:
input: kilo
types: address
components: country:so
sessiontoken: abc123
For details, copy a place_id from the autocomplete result and test:
place_id: the selected place ID
fields: formatted_address,address_components,geometry
sessiontoken: abc123
After both tests pass, create a connection from the connector and enter the Google API key when prompted.
Then open the canvas app, go to Data, add the new connection, and select the connection you just created. It will appear as a premium connector.
If you change the custom connector definition later, remove and add the connection again in Power Apps so the app refreshes the connector schema.
Build the Canvas Component
Create a canvas component with:
- text input:
txtAddress - gallery:
galAddressSuggestions - output property:
SelectedAddress - output property:
PlaceId - output property:
Latitude - output property:
Longitude - output property:
AddressJson
Set the gallery Items property to:
colAddressSuggestions
Inside the gallery, show:
ThisItem.structured_formatting.main_text
and:
ThisItem.structured_formatting.secondary_text
If the structured formatting fields are blank in your connector response, use ThisItem.description as the display value.
Get Suggestions While the User Types
For a simple build, use the text input OnChange property:
If(
Len(Trim(txtAddress.Text)) < 3,
Clear(colAddressSuggestions),
If(
IsBlank(varAddressSessionToken),
Set(varAddressSessionToken, GUID())
);
ClearCollect(
colAddressSuggestions,
Thegana.GetAddressPredictions(
txtAddress.Text,
"address",
"country:us",
varAddressSessionToken
).predictions
)
)
For better autocomplete behavior, set txtAddress.DelayOutput to true. That reduces connector calls while the user is still typing.
If your connector generated record-style parameters instead of positional parameters, use this shape:
ClearCollect(
colAddressSuggestions,
Thegana.GetAddressPredictions(
{
input: txtAddress.Text,
types: "address",
components: "country:us",
sessiontoken: varAddressSessionToken
}
).predictions
)
Use the country filter that fits the app. For Somalia, use:
country:so
For the United States, use:
country:us
Get Details After Selection
Set the gallery OnSelect property to call Place Details and parse the selected address.
Set(varSelectedPrediction, ThisItem);
Set(
varPlaceDetails,
Thegana.GetPlaceDetails(
varSelectedPrediction.place_id,
"formatted_address,address_components,geometry",
varAddressSessionToken
).result
);
ClearCollect(
colAddressComponents,
varPlaceDetails.address_components
);
Set(varFormattedAddress, varPlaceDetails.formatted_address);
Set(varPlaceId, varSelectedPrediction.place_id);
Set(varLatitude, varPlaceDetails.geometry.location.lat);
Set(varLongitude, varPlaceDetails.geometry.location.lng);
Set(varAddressJson, JSON(varPlaceDetails, JSONFormat.IndentFour));
Set(
varStreetNumber,
Coalesce(
LookUp(
colAddressComponents,
CountIf(types, Value = "street_number") > 0
).long_name,
""
)
);
Set(
varRoute,
Coalesce(
LookUp(
colAddressComponents,
CountIf(types, Value = "route") > 0
).long_name,
""
)
);
Set(
varStreet,
Trim(varStreetNumber & " " & varRoute)
);
Set(
varCity,
Coalesce(
LookUp(
colAddressComponents,
CountIf(types, Value = "locality") > 0
).long_name,
LookUp(
colAddressComponents,
CountIf(types, Value = "postal_town") > 0
).long_name,
LookUp(
colAddressComponents,
CountIf(types, Value = "administrative_area_level_2") > 0
).long_name
)
);
Set(
varState,
LookUp(
colAddressComponents,
CountIf(types, Value = "administrative_area_level_1") > 0
).short_name
);
Set(
varZip,
LookUp(
colAddressComponents,
CountIf(types, Value = "postal_code") > 0
).long_name
);
Set(
varCountry,
LookUp(
colAddressComponents,
CountIf(types, Value = "country") > 0
).long_name
);
Clear(colAddressSuggestions);
Set(varAddressSessionToken, Blank());
The main correction here is that GetPlaceDetails returns an object with a result property. The address components live inside:
varPlaceDetails.address_components
So the app should collect result.address_components, not the whole details response.
If your connector uses record-style parameters for Place Details, the call becomes:
Set(
varPlaceDetails,
Thegana.GetPlaceDetails(
{
place_id: varSelectedPrediction.place_id,
fields: "formatted_address,address_components,geometry",
sessiontoken: varAddressSessionToken
}
).result
);
Wire the Component Outputs
For the component output properties, return the parsed variables:
SelectedAddress = varFormattedAddress
PlaceId = varPlaceId
Latitude = varLatitude
Longitude = varLongitude
AddressJson = varAddressJson
You can also expose individual component outputs for:
StreetCityStatePostalCodeCountry
That makes the component more useful when the parent screen needs to patch separate SharePoint or Dataverse columns.
Common Issues
If suggestions are not returned, check these first:
- Places API is enabled in the Google Cloud project.
- Billing is configured.
- The API key is valid.
- The custom connector security parameter is named
key. - The autocomplete action uses
types, nottype. - The
componentsvalue uses a two-letter country code, such ascountry:soorcountry:us. - The app connection was removed and re-added after connector definition changes.
If the details call works but the city, postal code, or state is blank, inspect varAddressJson. Google does not return the exact same component types for every country or address. That is why the city formula checks locality, postal_town, and administrative_area_level_2.
Takeaway
The clean pattern is:
- Use Place Autocomplete while the user types.
- Reuse the same session token for the selected Place Details call.
- Collect
result.address_components. - Parse each address component by checking its
typestable. - Expose the final values from a reusable canvas component.
Once this is packaged into a component, the rest of the app does not need to know how Google Places works. The app just receives a selected address, a place ID, coordinates, and debug JSON when needed.