Get started with Search
Leveraging HERE's extensive global data set, which includes hundreds of millions of POIs and addresses worldwide, the HERE SDK enables fast and easy search functionality. With the HERE SDK, you can efficiently tackle a variety of search-related tasks using a single SearchEngine:
- Discover places: Search and discover places from HERE's vast database worldwide, either by category or by setting a search term.
- Generate auto suggestions: Search for places while typing a search term to offer query completion.
- Reverse geocode an address: Find the address that belongs to certain geographic coordinates.
- Geocode an address: Find the geographic coordinates correlated to an address.
- Search by ID: Search for a place identified by a HERE Place ID.
- Search along a route: Search for places along an entire route.
- Search by category along a route: Search for places based on categories along an entire route. This feature is in beta state.
- Support for what3words (only available for Navigate): Search with the help of three word addresses.
- Search offline (only available for Navigate): When no internet connection is available, you can switch to the
OfflineSearchEngineto search on already cached map data or preloaded offline maps data. - My places (only available for Navigate): Add your own places at runtime to search for them offline.
One feature that all search variants have in common is that you can specify the location or area where you want to search. Setting an area can be done by passing in a rectangle area specified by a GeoBox or even a circle area specified by a GeoCircle. Any potential search result that lies outside the specified area is ranked with lower priority, except for relevant global results - for example, when searching for "Manhattan" in Berlin. The underlying search algorithms are optimized to help narrow down the list of results to provide faster and more meaningful results to the user.
Search is based on HERE Geocoding and Search.
Example based on Geocoding & SearchSearchEngine.searchByAddress(AddressQuery, ...).
Example based on Reverse Geocode: SearchEngine.searchByCoordinates(GeoCoordinates, ...)
Example based on Discover & Search: SearchEngine.searchByText(TextQuery, ...).
Example based on Autosuggest: SearchEngine.suggestByText(TextQuery, ...).
NoteFor information about the pricing of these features, see the HERE Base Plan Pricing. If you are using the Navigate license or have other questions about pricing, contact us.
NoteEach search request is performed asynchronously. An online connection is required when the SearchEngine is used to fetch results from HERE's backend services.
Initialize the SearchEngine
The massive database of places provided by HERE's Location Services can be easily accessed and explored using the HERE SDK's SearchEngine. Let's look at an example. Begin by creating a new SearchEngine instance:
try {
searchEngine = new SearchEngine();
} catch (InstantiationErrorException e) {
throw new RuntimeException("Initialization of SearchEngine failed: " + e.error.name());
}try {
searchEngine = SearchEngine()
} catch (e: InstantiationErrorException) {
throw RuntimeException("Initialization of SearchEngine failed: " + e.error.name)
}Creating a new SearchEngine instance can throw an InstantiationErrorException that you have to handle as shown above. Note that it is not possible to initialize an engine during the Application's onCreate() lifecycle. Any other point in time is fine. For example, a good place to initialize an engine may be in an Activity's onCreate() method.
Enrich EV charging searches (EVCP 3.0)
Note
EVSearchEngineis online only and needs an additional HERE EV Charge Points API v3 license on top of a Navigate license. Without that entitlement, the API will not return charging metadata.
EVSearchEngine can run independently to fetch EV charging location details by HERE Place IDs, or it can be attached to SearchEngine via SearchEngine.setEVInterface(...) so that every standard place search automatically exposes place.getDetails().evChargingLocation. Choose the mode that fits your use case: if you only need EV-specific payload (tariffs, EVSE status, truck restrictions, etc.) call EVSearchEngine.search() directly; if you want EV metadata merged into broader POI responses, wire it into your existing SearchEngine instance.
EVSearchExample keeps both paths alive: it demonstrates wiring the engines together for enrichment, while searchEVChargingLocationsByPlaceIds() can also be invoked standalone when no SearchEngine query is involved.
public final class EVSearchExample {
// Provides EVCP 3.0 enrichment; requires an additional EV Charging license beyond Navigate.
private static final String LOG_TAG = "EVSearchExample";
private final SearchEngine searchEngine;
private final EVSearchEngine evSearchEngine;
public EVSearchExample() throws InstantiationErrorException {
searchEngine = new SearchEngine();
evSearchEngine = new EVSearchEngine();
searchEngine.setEVInterface(evSearchEngine);
}
// Works standalone for EV-only lookups or alongside SearchEngine to enrich generic results.
public void searchEVChargingLocationsByPlaceIds(List<String> placeIds,
SearchOptions searchOptions) {
EVSearchOptions evOptions = new EVSearchOptions();
evOptions.additionalFeatures = Arrays.asList(
EVChargingLocationFeature.ENERGY_MIX,
EVChargingLocationFeature.OPENING_HOURS);
// Direct EVSearchEngine call (can be used without SearchEngine involvement).
evSearchEngine.setOptions(evOptions);
evSearchEngine.search(placeIds, (evSearchError, locations) -> {
if (evSearchError != null) {
Log.d(LOG_TAG, "EV lookup failed: " + evSearchError);
return;
}
for (EVChargingLocation location : locations) {
Metadata metadata = new Metadata();
metadata.setString("id", location.getID());
metadata.setString("connectorGroups", String.valueOf(location.getConnectorGroups().size()));
// Attach metadata to dedicated EV UI or map markers.
}
}, evOptions);
// Optional: keep using SearchEngine so every place result surfaces evChargingLocation details.
searchEngine.searchByText(new TextQuery("charging", new TextQuery.Area(getMapViewGeoBox())),
searchOptions, (searchError, places) -> {
if (searchError != null) {
Log.d(LOG_TAG, "Search error: " + searchError);
return;
}
for (Place place : places) {
Details details = place.getDetails();
EVChargingLocation evDetails = details != null ? details.evChargingLocation : null;
// evDetails is populated only when the EV license is active.
}
});
}
}class EVSearchExample @Throws(InstantiationErrorException::class) constructor() {
// Provides EVCP 3.0 enrichment; requires an additional EV Charging license beyond Navigate.
private val searchEngine = SearchEngine()
private val evSearchEngine = EVSearchEngine()
init {
searchEngine.setEVInterface(evSearchEngine)
}
// Works standalone for EV-only lookups or alongside SearchEngine to enrich generic results.
fun searchEVChargingLocationsByPlaceIds(
placeIds: List<String>,
searchOptions: SearchOptions
) {
val evOptions = EVSearchOptions().apply {
additionalFeatures = listOf(
EVChargingLocationFeature.ENERGY_MIX,
EVChargingLocationFeature.OPENING_HOURS
)
}
// Direct EVSearchEngine call (can be used without SearchEngine involvement).
evSearchEngine.setOptions(evOptions)
evSearchEngine.search(placeIds, { error, locations ->
if (error != null) {
Log.d("EVSearchExample", "EV lookup failed: $error")
return@search
}
locations?.forEach { location ->
val metadata = Metadata().apply {
setString("id", location.id)
setString("connectorGroups", location.connectorGroups.size.toString())
}
// Attach metadata to dedicated EV UI or map markers.
}
}, evOptions)
// Optional: use SearchEngine to enrich general search results with EV metadata.
searchEngine.searchByText(TextQuery("charging", TextQuery.Area(mapViewGeoBox)),
searchOptions) { searchError, places ->
if (searchError != null) {
Log.d("EVSearchExample", "Search error: $searchError")
return@searchByText
}
places?.forEach { place ->
val evDetails = place.details?.evChargingLocation
// evDetails is populated only when the EV license is active.
}
}
}
}The same EVSearchExample implementation ships with the EVRouting example app so you can see it running end-to-end.
With this setup you can continue to use the familiar SearchEngine entry points (category, text, or along-route searches) while automatically surfacing EVCP 3.0 information such as connector counts, tariffs, and operator data wherever applicable. If you only need the EV-specific payload, you can rely solely on EVSearchEngine.
Search for places
Let's assume you want to find all "pizza" places around the current map center shown on the device. Before starting the search, you need to specify a few more details:
SearchOptions searchOptions = new SearchOptions();
searchOptions.languageCode = LanguageCode.EN_US;
searchOptions.maxItems = 30;val searchOptions = SearchOptions()
searchOptions.languageCode = LanguageCode.EN_US
searchOptions.maxItems = 30Here, you create a new SearchOptions object holding the desired data:
- You can specify the language of the returned search results by setting a
LanguageCode. maxItemsis set to define the maximum number of result items that should be delivered in the response.
In the example above, you limit the results to 30. If the engine discovers more search results than requested, it will return only the 30 most relevant search results.
You perform an area search (one-box) as you want to find all results within the current viewport. The SearchEngine provides three different ways to specify the search location:
- Search at
GeoCoordinates: Performs an asynchronous search request around the specified coordinates to provide the most relevant search results nearby. - Search in a
GeoCirclearea: Similar to the above, but searches for results within the specified circle area, which is defined by center geographic coordinates and a radius in meters. - Search in a
GeoBoxarea: Similar to the above, but searches for results within the specified rectangle area, which is defined by the southwest and northeast coordinates passed as parameters.
A one-box search is ideal to discover places nearby. As input you can provide a free-form text in various and mixed languages (such as Latin, Cyrillic, Arabic, Greek).
You can specify the area and the term you want to search for together. For example, below you can set queryString to "pizza":
GeoBox viewportGeoBox = getMapViewGeoBox();
TextQuery.Area queryArea = new TextQuery.Area(viewportGeoBox);
TextQuery query = new TextQuery(queryString, queryArea);val viewportGeoBox = mapViewGeoBox
val queryArea = TextQuery.Area(viewportGeoBox)
val query = TextQuery(queryString, queryArea)Here we have left out the code for getMapViewGeoBox(). You can create and pass in any GeoBox that fits to your use case. A possible implementation can be found in the accompanying example apps.
Firstly, the results within the specified map area are returned. If no results were found, global search results may be returned. However, relevant global results such as prominent cities or states may be included - regardless of the specified search location.
NoteThe query string can contain any textual description of the content you want to search for. You can pass in several search terms to narrow down the search results - with or without comma separation. So, "Pizza Chausseestraße" and "Pizza, Chausseestraße" will both lead to the same results and will find only pizza restaurants that lie on the street 'Chausseestraße'. Please also note that it is an error to pass in an empty query string, and in this case, the search will fail.
Finally, you can start to search asynchronously:
searchEngine.searchByText(query, searchOptions, querySearchCallback);
...
private final SearchCallback querySearchCallback = new SearchCallback() {
@Override
public void onSearchCompleted(@Nullable SearchError searchError, @Nullable List<Place> list) {
if (searchError != null) {
showDialog("Search", "Error: " + searchError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
showDialog("Search", "Results: " + list.size());
// Add new marker for each search result on map.
for (Place searchResult : list) {
// ...
}
}
};searchEngine!!.searchByText(query, searchOptions, querySearchCallback)
...
private val querySearchCallback =
SearchCallback { searchError, list ->
if (searchError != null) {
showDialog("Search", "Error: $searchError")
return@SearchCallback
}
// If error is null, list is guaranteed to be not empty.
showDialog("Search", "Results: " + list!!.size)
// Add new marker for each search result on map.
for (searchResult in list) {
val metadata = Metadata()
metadata.setCustomValue("key_search_result", SearchResultMetadata(searchResult))
// Note: getGeoCoordinates() may return null only for Suggestions.
addPoiMapMarker(searchResult.geoCoordinates!!, metadata)
}
}Before you can look into the results, first you should check for a possible SearchError. For example, if the device is offline, the list will be null and the error enum will indicate the cause. In this case, you call a helper method showDialog() to show the error description to the user. A possible implementation of showDialog() can be accessed from the accompanying "Search" example's source code - it does not contain any HERE SDK specific code.
NoteThe search response contains either an error or a result:
SearchErrorandList<Place>. Both can't be null at the same time - or non-null at the same time.
Show the results on the map
Now, it's time to look into the results. If no matching results could be found, an error would have been caught beforehand:
// If error is null, list is guaranteed to be not empty.
showDialog("Search", "Results: " + list.size);
// Add new marker for each search result on map.
for (Place searchResult : list) {
Metadata metadata = new Metadata();
metadata.setCustomValue("key_search_result", new SearchResultMetadata(searchResult));
// Note: getGeoCoordinates() may return null only for Suggestions.
addPoiMapMarker(searchResult.getGeoCoordinates(), metadata);
}
...
// Class to store search results as Metadata.
private static class SearchResultMetadata implements CustomMetadataValue {
public final Place searchResult;
public SearchResultMetadata(Place searchResult) {
this.searchResult = searchResult;
}
@NonNull
@Override
public String getTag() {
return "SearchResult Metadata";
}
}// If error is null, list is guaranteed to be not empty.
showDialog("Search", "Results: " + list!!.size)
// Add new marker for each search result on map.
for (searchResult in list) {
val metadata = Metadata()
metadata.setCustomValue("key_search_result", SearchResultMetadata(searchResult))
// Note: getGeoCoordinates() may return null only for Suggestions.
addPoiMapMarker(searchResult.geoCoordinates!!, metadata)
}
...
// Class to store search results as Metadata.
private class SearchResultMetadata(val searchResult: Place) : CustomMetadataValue {
override fun getTag(): String {
return "SearchResult Metadata"
}
}Finally, you can iterate over the list of results. Each Place contains various fields describing the found search result.
In our example, to add a marker to the map, you are interested in the place's location. In addition, you create a Metadata object where you can store a SearchResult.
NoteThe
Metadataobject can contain various data types to allow easy association of aMapMarkerwith the result data. This way, you can hold all information related to a map marker in one object - this can be convenient when presenting this data, for example, after the user taps on a map marker. Even complex data objects can be stored by implementing theCustomMetadataValueinterface, as shown above.
A possible implementation of addPoiMapMarker() can be accessed from the accompanying "Search" example's source code; see also the section about MapMarker in this guide.
private void addPoiMapMarker(GeoCoordinates geoCoordinates, Metadata metadata) {
MapMarker mapMarker = createPoiMapMarker(geoCoordinates);
mapMarker.setMetadata(metadata);
mapView.getMapScene().addMapMarker(mapMarker);
mapMarkerList.add(mapMarker);
}
private MapMarker createPoiMapMarker(GeoCoordinates geoCoordinates) {
MapImage mapImage = MapImageFactory.fromResource(context.getResources(), R.drawable.poi);
return new MapMarker(geoCoordinates, mapImage, new Anchor2D(0.5F, 1));
}private fun addPoiMapMarker(geoCoordinates: GeoCoordinates, metadata: Metadata) {
val mapMarker = createPoiMapMarker(geoCoordinates)
mapMarker.metadata = metadata
mapView.mapScene.addMapMarker(mapMarker)
mapMarkerList.add(mapMarker)
}
private fun createPoiMapMarker(geoCoordinates: GeoCoordinates): MapMarker {
val mapImage = MapImageFactory.fromResource(context.resources, R.drawable.poi)
return MapMarker(geoCoordinates, mapImage, Anchor2D(0.5, 1.0))
}After you have at hand the picked map marker object, you can get the Metadata information that you have set in the previous step:
Metadata metadata = topmostMapMarker.getMetadata();
if (metadata != null) {
CustomMetadataValue customMetadataValue = metadata.getCustomValue("key_search_result");
if (customMetadataValue != null) {
SearchResultMetadata searchResultMetadata = (SearchResultMetadata) customMetadataValue;
String title = searchResultMetadata.searchResult.getTitle();
String vicinity = searchResultMetadata.searchResult.getAddress().addressText;
showDialog("Picked Search Result",title + ". Vicinity: " + vicinity);
return;
}
}val metadata = topmostMapMarker.metadata
if (metadata != null) {
val customMetadataValue = metadata.getCustomValue("key_search_result")
if (customMetadataValue != null) {
val searchResultMetadata = customMetadataValue as SearchResultMetadata
val title = searchResultMetadata.searchResult.title
val vicinity = searchResultMetadata.searchResult.address.addressText
showDialog("Picked Search Result", "$title. Vicinity: $vicinity")
return@MapPickCallback
}
}Not all map markers may contain Metadata. Unless you have set the Metadata beforehand, getMetadata() will return null. In this example, you simply check if the data stored for "key_search_result" is not null, so that you know it must contain search data. You can then downcast to our custom type SearchResultMetadata which holds the desired Place.
Consult the API Reference for a complete overview on the available nullable fields.

Screenshot: Showing a picked search result with title and vicinity.
Access web images
The HERE SDK provides a list of WebImage objects in place.Details, allowing visual exploration of each Place.
NoteTo enrich search results with web images, valid credentials are required to receive rich content from TripAdvisor. Contact your HERE representative to request access. If the license is missing, the images list will be empty.
WebImage access can be enabled like this:
searchEngine.setCustomOption("discover.show", "tripadvisor");searchEngine.setCustomOption("discover.show", "tripadvisor")Once WebImage access is enabled and valid credentials for rich content are in place, images will be included in the search results. To access a WebImage from a Place, use the following approach:
public void handleWebImages(Place searchResult){
List<WebImage> webImages = searchResult.getDetails().images;
for (WebImage webImage: webImages){
Log.i(LOG_TAG,"WebImage found for place :" +
searchResult.getTitle().trim() +
". Link: " + webImage.source.href);
}
}private fun handleWebImages(searchResult: Place) {
val webImages = searchResult.details.images
for (webImage in webImages) {
Log.i(LOG_TAG, "WebImage found for place :" +
searchResult.title.trim() +
". Link: " + webImage.source.href)
}
}Users can retrieve images by accessing the href in each webImage.Source using platform-specific in-app logic. For example a href link can look like this: https://media-cdn.tripadvisor.com/media/photo-f/10/fb/d1/05/panorama.jpg.
Zoom to places
The above code uses a GeoBox to search directly in the shown map viewport, so it is not necessary to zoom to the found results. Instead of a GeoBox you can search also around a location ("search around me"), inside a GeoCircle, along a GeoCorridor or inside countries by passing a list of CountryCode values. See TextQuery.Area for all supported types.
If the search area is not equal to the shown map viewport, you can use the following code to fly to the results by creating a GeoBox from a list of GeoCoordinates. Get the GeoBox from GeoBox.containing(geoCoordinatesList).
// Set null values to keep the default map orientation.
mapCamera.lookAt(geoBox, new GeoOrientationUpdate(null, null));This instantly moves the camera. If desired, you can also apply various animation styles to fly the camera to a desired area. Look at the MapCamera section and check the "CameraKeyFrameTracks" example app on GitHub.
If you want to apply an additional padding, use an overloaded lookAt() method that accepts a viewRectangle as additional parameter. Note that the rectangle is specified in pixels referring to the map view inside which the GeoBox is displayed.
Point2D origin = new Point2D(5, 5);
Size2D sizeInPixels = new Size2D(mapView.getWidth() - 10, mapView.getHeight() - 10);
Rectangle2D paddedViewRectangle = new Rectangle2D(origin, sizeInPixels);The above code creates a rectangle that can be used to add a 5 pixel padding around any GeoBox that is shown in the map viewport.
Try the Search example app
You can find the code for the above and the following sections as part of the Search example app on GitHub.
Search for what3words (only available for Navigate)
What3words is a geocoding system that assigns a unique three-word address to every three-meter square on earth. It allows a precise identification of locations through easy-to-remember word combinations and can also convert these addresses back to their exact geographic coordinates.
Let's look at an example. You can create a new W3WSearchEngine instance like so:
try {
w3wSearchEngine = new W3WSearchEngine();
} catch (InstantiationErrorException e) {
throw new RuntimeException("Initialization of W3WSearchEngine failed: " + e.error.name());
}try {
w3wSearchEngine = W3WSearchEngine()
} catch (e: InstantiationErrorException) {
throw RuntimeException("Initialization of W3WSearchEngine failed: " + e.error.name)
}Creating a new W3WSearchEngine instance can throw an InstantiationErrorException. Note that it is not possible to initialize an engine during the Application's onCreate() lifecycle. Any other point in time is fine. For example, a good place to initialize an engine may be in an Activity's onCreate() method.
NoteYou can find the example code for the
W3WSearchEngineas part of the "SearchHybrid" example app, provided in Java and Kotlin on GitHub.
Find the location for a given what3words term (only available for Navigate)
Start by setting searchWords. Then invoke the search method of the w3wSearchEngine, supplying the term.
Upon completing the search, the onW3WSearchCompleted() method processes the outcome.
// Sample "dizzy.vanilla.singer" used for demonstration purposes. Replace with user input as needed.
String searchWords = "dizzy.vanilla.singer";
/* Finds the location of a known What3Words term.
* This method searches for the geographic location corresponding to a given three-word address
* (e.g., "dizzy.vanilla.singer").
*/
w3wSearchEngine.search(searchWords, new W3WSearchCallback() {
@Override
public void onW3WSearchCompleted(@Nullable W3WSearchError w3WSearchError, @Nullable W3WSquare w3WSquare) {
handleW3WSearchResult(w3WSearchError, w3WSquare);
}
});
private void handleW3WSearchResult(@Nullable W3WSearchError w3WSearchError, @Nullable W3WSquare w3WSquare) {
if (w3WSearchError != null) {
showDialog("W3Words Search Error", "Error: " + w3WSearchError.toString());
} else if (w3WSquare != null) {
// If the search was successful, extract the What3Words.
String W3Words = w3WSquare.words;
// Retrieve additional details, such as the coordinates of the square.
GeoCoordinates southWestCorner = w3WSquare.square.southWestCorner;
GeoCoordinates northEastCorner = w3WSquare.square.northEastCorner;
// Check if the details are available and display them in a dialog.
showDialog("W3Words Details",
"W3Words: " + W3Words + "\n" +
"Language: " + w3WSquare.languageCode + "\n" +
"southWestCorner coordinates: " + southWestCorner.latitude + ", " + southWestCorner.longitude + "\n" +
"northEastCorner coordinates: " + northEastCorner.latitude + ", " + northEastCorner.longitude);
}
}Geocode a what3words term to a location (only available for Navigate)
Start by initializing a GeoCoordinates object with specified latitude and longitude values, along with a designated language code. Then invoke the search method of the w3wSearchEngine, supplying the coordinates, language, and a callback to manage the response.
Upon completing the geocoding search, the onW3WSearchCompleted() method processes the outcome.
GeoCoordinates geoCoordinates = new GeoCoordinates(52.520798, 13.409408);
// The language code for the desired three-word address.
// ISO 639-1 code "en" specifies that the three-word address will be in English.
String w3wLanguageCode = "en";
/* Resolves geographic coordinates to a What3Words address (three-word format).
* This method uses the What3Words search engine to find a three-word address based
* on the provided coordinates (latitude and longitude).
*/
w3wSearchEngine.search(geoCoordinates, w3wLanguageCode, new W3WSearchCallback() {
@Override
public void onW3WSearchCompleted(@Nullable W3WSearchError w3WSearchError, @Nullable W3WSquare w3WSquare) {
handleW3WSearchResult(w3WSearchError, w3WSquare);
}
});Updated 6 hours ago