Best Practices Using Geolocation API Web Services

The Google Maps Platform web services are a collection of HTTP interfaces to Google services providing geographic data for your maps applications.

This guide describes some common practices useful for setting up your web service requests and processing service responses. Refer to the developer’s guide for full documentation of the Geolocation API.

What is a web service?

Google Maps Platform web services are an interface for requesting Maps API data from external services and using the data within your Maps applications. These services are designed to be used in conjunction with a map, as per the License Restrictions in the Google Maps Platform Terms of Service.

The Maps APIs web services use HTTP(S) requests to specific URLs, passing URL parameters and/or JSON-format POST data as arguments to the services. Generally, these services return data in the HTTP(S) request as JSON for parsing and/or processing by your application.

Geolocation requests are sent using POST to the following URL:

https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_API_KEY

Note: All Geolocation API applications require authentication. Get more information on authentication credentials.

SSL/TLS Access

HTTPS is required for all Google Maps Platform requests that use API keys or contain user data. Requests made over HTTP that contain sensitive data may be rejected.

Polite Use of Google APIs

Poorly designed API clients can place more load than necessary on both the Internet and Google's servers. This section contains some best practices for clients of the APIs. Following these best practices can help you avoid your application being blocked for inadvertent abuse of the APIs.

Exponential Backoff

In rare cases something may go wrong serving your request; you may receive a 4XX or 5XX HTTP response code, or the TCP connection may simply fail somewhere between your client and Google's server. Often it is worthwhile re-trying the request as the followup request may succeed when the original failed. However, it is important not to simply loop repeatedly making requests to Google's servers. This looping behavior can overload the network between your client and Google causing problems for many parties.

A better approach is to retry with increasing delays between attempts. Usually the delay is increased by a multiplicative factor with each attempt, an approach known as Exponential Backoff.

For example, consider an application that wishes to make this request to the Time Zone API:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

The following Python example shows how to make the request with exponential backoff:

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

You should also be careful that there isn't retry code higher in the application call chain that leads to repeated requests in quick succession.

Synchronized Requests

Large numbers of synchronized requests to Google's APIs can look like a Distributed Denial of Service (DDoS) attack on Google's infrastructure, and be treated accordingly. To avoid this, you should make sure that API requests are not synchronized between clients.

For example, consider an application that displays the time in the current time zone. This application will probably set an alarm in the client operating system waking it up at the start of the minute so that the displayed time can be updated. The application should not make any API calls as part of the processing associated with that alarm.

Making API calls in response to a fixed alarm is bad as it results in the API calls being synchronized to the start of the minute, even between different devices, rather than being distributed evenly over time. A poorly designed application doing this will produce a spike of traffic at sixty times normal levels at the start of each minute.

Instead, one possible good design is to have a second alarm set to a randomly chosen time. When this second alarm fires the application calls any APIs it needs and stores the results. When the application wants to update its display at the start of the minute, it uses previously stored results rather than calling the API again. With this approach, API calls are spread evenly over time. Further, the API calls do not delay rendering when the display is being updated.

Aside from the start of the minute, other common synchronization times you should be careful not to target are at the start of an hour, and the start of each day at midnight.

Processing Responses

This section discusses how to extract these values dynamically from web service responses.

The Google Maps web services provide responses which are easy to understand, but not exactly user friendly. When performing a query, rather than display a set of data, you probably want to extract a few specific values. Generally, you will want to parse responses from the web service and extract only those values which interest you.

The parsing scheme you use depends on whether you are returning output in JSON. JSON responses, being already in the form of Javascript objects, may be processed within Javascript itself on the client.

Processing JSON

JSON (Javascript Object Notation) has an obvious advantage over XML in that the response is lightweight. Parsing such a result is trivial in JavaScript as the format is already a valid Javascript object. For example, to extract the value of the 'formatted_address' keys within a JSON result object, simply access them using the following code:

for (i = 0; i < myJSONResult.results.length; i++) {
  myAddress[i] = myJSONResult.results[i].formatted_address;
}

Note that because JSON may contain multiple values, it's wisest to iterate over the length of the results array if you want to capture all possible values. In practice, you may wish to only return the first result (results[0]), however.

Parsing JSON in other languages is only moderately more difficult. The following Python example initiates a Geocoding Web Service request and displays all resulting formatted_address values to the user within an array:

import json
import urllib.request


# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/geocoding/get-api-key
API_KEY = "YOUR_KEY_HERE"
GEOCODE_BASE_URL = "https://maps.googleapis.com/maps/api/geocode/json"


def geocode(address):
    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode({"address": address, "key": API_KEY,})
    url = f"{GEOCODE_BASE_URL}?{params}"

    result = json.load(urllib.request.urlopen(url))

    if result["status"] in ["OK", "ZERO_RESULTS"]:
        return result["results"]

    raise Exception(result["error_message"])


if __name__ == "__main__":
    results = geocode(address="San+Francisco")
    print(json.dumps([s["formatted_address"] for s in results], indent=2))

Output: [ "San Francisco, CA, USA" ]