Migrating from Google Places to HERE Geocoding and Search
If you're a current user of Google's Places service, you may be interested in switching over to HERE's Geocoding and Search product. We recently did a pretty big update to the service (see Touring our New Geocoding and Search API for some examples) so now is a great time to talk about how developers can migrate from one to the other.
Broadly speaking, Google's Places API is split across a few different services:
- Place Search and Details - lets you either search for locations based on text input and/or location or returns information about one particular location.
- Autocomplete - which provides both matches for an input as well as possible query terms to suggest.
- Photos - returns photos of locations.
In this guide I'm going to focus on the first of the two features above. Currently we do not support photos of locations but we constantly look for ways to improve our APIs and data so be sure to check later. Let's begin by talking about search.
Finding Locations
Google's place search is broken across multiple different APIs:
- "Find Place", which takes user input and returns one place.
- "Nearby Search", which takes a location and optional user input to return a list of places.
- "Text Search", which takes user input that may or may not contain partial address information along with option location data and attempts to find places that match.
What is returned for each service is a bit different. Find Place returns the bare minimum by default unless you ask or additional fields. Some fields are "free" (no additional charge outside of the regular API cost) but then there are two "levels" of fields with increasing costs. The later two APIs return more information in their results which touch upon the higher tier pricing. Their documentation includes this warning to that effect:
"Nearby Search and Text Search return all of the available data fields for the selected place (a subset of the supported fields), and you will be billed accordingly There is no way to constrain Nearby Search or Text Search to only return specific fields. To keep from requesting (and paying for) data that you don't need, use a Find Place request instead."
Unfortunately, as stated above "Find Place" does just that, returns "a" place. Their docs for nearby search pricing say this:
Nearby Search requests return a list of places, but do not support specifying which fields are returned. Nearby Search requests return a subset of the supported data fields. You are charged for the Nearby Search request starting at 0.032 USD per each, as well as all of the data-type SKUs (Basic Data, Contact Data, and Atmosphere Data).
So to be clear, that's a cost for using the API as well as a cost for getting additional data about locations found. Conversely, HERE does not charge for particular fields of data in our results and will only charge based on use of the API itself.
With that out of the way, let's consider a few common scenarios and compare how to do them in Google Places versus HERE. We'll consider the following examples:
- Based on my location (which we will hard code to the same value), look for the string "cajun".
- Based on my location (again, hard coded to a value), look for the string "cajun" and specify a restaurant filter.
- Given one result, ask for details.
Let's start with the inital example, finding places with the string "cajun" using the Google Places API, Nearby Search. Your URL could look like so:
https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=KEY&keyword=cajun&location=30.22,-92.02&radius=5000
Outside of the key, the important bits are the keyword
, location
, and radius
values. The result object below shows the result with most of the results removed to keep the size down a bit.
{
"html_attributions": [],
"results": [
{
"geometry": {
"location": {
"lat": 30.230667,
"lng": -92.030914
},
"viewport": {
"northeast": {
"lat": 30.23192767989273,
"lng": -92.02973862010728
},
"southwest": {
"lat": 30.22922802010729,
"lng": -92.03243827989273
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "0c0d0de636f6a00c591ead4014591c036671da3f",
"name": "Julien's Po Boys",
"opening_hours": {
"open_now": true
},
"photos": [
{
"height": 2979,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/101792845719304682703\">Julien's Po-Boys</a>"
],
"photo_reference": "CmRaAAAAjssr4VOcn7z5RiA6vXB_a1lcJwUOcakkV_RZ_oTLdNiXga_u7p0W0PbS0BhrCBSMo9ZgEVlcc9XttwQ4Ma7qHRgWKhNGuYB13U8EtNpyQn1c8_7iCCuzL9w5UjYyCXghEhDDByB796grq_v63n4MTG2VGhRVPluVxc25vi-JNzLzf4dv3pv65g",
"width": 3024
}
],
"place_id": "ChIJa2a_l36cJIYRwXBcb1idJ3g",
"plus_code": {
"compound_code": "6XJ9+7J Lafayette, Parish Governing Authority District B, LA",
"global_code": "86296XJ9+7J"
},
"price_level": 2,
"rating": 4,
"reference": "ChIJa2a_l36cJIYRwXBcb1idJ3g",
"scope": "GOOGLE",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
],
"user_ratings_total": 182,
"vicinity": "1900 W University Ave, Lafayette"
},
// ITEMS REMOVED HERE!
],
"status": "OK"
}
HERE provides two separate search APIs. Discover is used for free form text searching where results are ranked by how well they match the search term. Browse supports searching as well but is better suited for finding results within categories. Browse results are always sorted by distance.
For this first use case, it makes sense to use Discover. Here's how that URL would look:
https://discover.search.hereapi.com/v1/discover?apikey=KEY&q=cajun&at=30.22,-92.02
The two important parameters here are q
and at
. Note that the Google API requires a center location and a radius. HERE's API can support that as well by using the in
parameter instead.
https://discover.search.hereapi.com/v1/discover?apikey={{apiKey}}&q=cajun&in=circle:30.22,-92.2;r=5000
See our API docs for more information about the in
parameter and others. Here's the result of the above call, again with many entries removed to keep the size down a bit.
{
"items": [
{
"title": "Lucky Deuces Casino",
"id": "here:pds:place:8403fv6k-895e95ca19bf067af48f3b2a6ba50ada",
"resultType": "place",
"address": {
"label": "Lucky Deuces Casino, 3002 Daulat Dr, Duson, LA 70529, United States",
"countryCode": "USA",
"countryName": "United States",
"state": "Louisiana",
"county": "Acadia",
"city": "Duson",
"street": "Daulat Dr",
"postalCode": "70529",
"houseNumber": "3002"
},
"position": {
"lat": 30.24588,
"lng": -92.19461
},
"access": [
{
"lat": 30.24535,
"lng": -92.1946
}
],
"distance": 2924,
"categories": [
{
"id": "100-1000-0000"
},
{
"id": "200-2300-0021"
},
{
"id": "600-6000-0061"
}
],
"foodTypes": [
{
"id": "101-070"
}
],
"contacts": [
{
"phone": [
{
"value": "+13378735400"
},
{
"value": "+13378738500"
}
],
"www": [
{
"value": "http://www.lucky2s.com/"
}
],
"email": [
{
"value": "luckydeucescasino@gmail.com"
}
]
}
],
"openingHours": [
{
"text": [
"Mon-Sun: 00:00 - 00:30"
],
"isOpen": true,
"structured": [
{
"start": "T000000",
"duration": "PT00H30M",
"recurrence": "FREQ:DAILY;BYDAY:MO,TU,WE,TH,FR,SA,SU"
}
]
}
]
},
// ITEMS REMOVED HERE
]
}
In terms of data returned, consider the following changes:
- At a top level, items in Google's API are found at the results level. For HERE it is in items.
- Result locations for Google are found at the geometry property. For HERE it is at position.
- Google does not return a human-readable address. HERE returns it as the address property.
- Google returns photos. As stated above, this is not a feature of our API (yet).
- Google returns a property, opening_hours.open_now which tells you if the location is open. The HERE API returns that as well as both human-readable hours open and machine parseable versions.
- Google doesn't return any contact information. HERE returns multiple forms of contact when available.
- Google returns information about the type of result in a
types
array. HERE returns an array of categories. Unfortunately they are in id form and you would need to translate them in your code. This will be improved soon! For restaurants, an additional array namedfoodTypes
is returned. You can find documentation for all id values here.
Now let's consider another example, imeplenting a category filter. In Google's Places API, this is done via the type
parameter. Here's how you would search for "cajun" within the restaurant category:
https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=KEY&keyword=cajun&location=30.22,-92.02&radius=5000 &type=restaurant
For HERE, you first need to figure out the category ID. You can find these documented here: Places Categories and Cuisines. The category ID for restaurant is 100-1000-0000. Therefore the URL would look like so:
https://browse.search.hereapi.com/v1/browse?apiKey=KEY&at=30.22,-92.02&categories=100-1000-0000&name=cajun
However, we support an *extremely* detailed food type categorization system as well. In that system you'll see there is a category, 101-070, that represents "American-Cajun". This means we can remove "cajun" from the text paramter and use the more specific ID instead:
https://browse.search.hereapi.com/v1/browse?apiKey=KEY&at=30.22,-92.02&categories=101-070
What about detail results? As described above, Google will return subsets of fields based on the type of search being used. They have a Place Details API to return more information, but again be aware of the different billing used for what fields are returned.
HERE does have an API to return specifics on one particular location (Lookup), but as our search APIs return the same information, you probably do not need to use this. (One small note, when searching you may get one additional field, distance
, that you do not get in the lookup API. It wouldn't make sense there.)
Keeping in mind you probably won't need to, here's how you would migrate a call from Google to HERE. First, the Google Places request:
https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJ65TLvh2dJIYRPFNC6EsaGqM&fields=name,rating,formatted_phone_number&key=KEY
Note that specific fields are requested. If you do not pass the fields
parameter, all fields are returned. (But again, remember you may be charge more for the call.)
Using the Lookup API, your request would look like so:
https://lookup.search.hereapi.com/v1/lookup?apikey=KEY&id=here:pds:place:8409vqef-c2e32cec334649f694d4e8ca75f8914a
It's probably obvious, but you can't use ID values from Google Places data to look up data in HERE's database.
Autosuggest
Both Google and HERE support an "autosuggest" feature that attempts to match partial user input, like "caj" to places of interest that match the term as well as fall within a certain range to be close enough to the user making the request. As the user types, the results should become even more helpful (hopefully) in terms of anticipating what the user is trying to find.
For both Google and HERE, you have access to a simple REST-based API to make direct network requests as well as a client-side library for mobile platforms and JavaScript. Here is an example of Google's REST-based API for autosuggest:
https://maps.googleapis.com/maps/api/place/autocomplete/json?input=caj&key=KEY&location=30.22,-92.02&radius=5000
Here's the corresponding version in HERE's autosuggest API:
https://autosuggest.search.hereapi.com/v1/autosuggest?apikey=KEY&q=${encodeURIComponent(text)}&at=30.22,-92.02
The main differences are changing input
to q
and location
to at
. As a reminder, HERE's services also let you specify circular ranges with a radius if you wish.
Because of the complexity of how your map may be built, we can't offer a simple replacement for using autosuggest with JavaScript applications, but you can see an example of that integration in our docs.
Wrap Up
Hopefully this migration article has explained how to transition from Google's APIs to our own, but if you need more help there's plenty of resources to be found. Be sure to check the documentation when you get started. You can join our developer community on Slack or ask your question on StackOverflow and someone from HERE or the community will reply.
Have your say
Sign up for our newsletter
Why sign up:
- Latest offers and discounts
- Tailored content delivered weekly
- Exclusive events
- One click to unsubscribe