Plan a route with multiple stops using the Routes API

In this blog post, I'll show you how to use the Google Routes API to build a Google Maps clone (demo / source code) that displays traffic conditions using color coded polylines. I’ll also provide an in depth look at the Compute Routes endpoint of the Routes API, explaining how you can use it to not only get the most efficient route between two points, but also retrieve traffic aware polylines, alternative route options, turn by turn directions, toll details, and must have metrics such as trip distances and estimated travel times.

Part 1: A developer's guide to the Google Routes API
Part 2: Plan a route with multiple stops using the Routes API (this article)
Part 3: Using the Google Distance Matrix API for taxi dispatch
Part 4: Google Search Along Route
Part 5: How to draw a route on Google Maps for Android with the Routes API

What is the Compute Routes endpoint of the Routes API?

The Compute Routes endpoint of the Routes API is the Google Maps Get Route API. It receives as input an origin and a destination, and returns the ideal route between these two locations.

The route returned is expressed as an encoded polyline that looks like this:

iggkHlrynVr@j@h@L~C`@b@ERKRYt@iBR{@Hw@FgBHcCRkCt@qNAuAe@mG{BoWW{FEkFDsp@@aTIcHU{Gi@uJESW}C[oCuFag@gBwPe@oCi@eBi@wAsA_Cq@y@o@m@}

When properly decoded using a polyline decoder or the Google Maps Geometry Library, the polyline converts into a list of latitude and longitude coordinate pairs, which can then be used to draw the route on a map (more on this later).

Routes API Field Masks

In addition to the route polyline, the Routes API can return all kinds of useful information about the route. What's included in the response depend on the fields specified in the X-Goog-FieldMask header in your request.

The response field mask is a comma-separated list of strings, where each string specifies a unique field in the response message. The list must not include any spaces.

routes.duration is the total time in seconds needed to complete the route (drive time). You can add this value to the departureTime from the request body to get the ETA, or Estimated Time of Arrival.

routes.distanceMeters is the total length of the route in meters.

routes.polyline is the route's encoded polyline string.

routes.legs.steps provides a detailed breakdown of the route into individual navigation steps each with a startLocation, endLocation, polyline and turn by turn directions,.

routes.travelAdvisory.tollInfo gives you an estimate of toll fees and other one-off charges incurred when using the route.

routes.travelAdvisory.speedReadingIntervals retrieves real time traffic speed data for different segments of the route.

💡
You can learn more about field masks and how they ensure you only access and pay for data that you need here: Choosing what fields to return.

Routes API example - how to make a route in Google Maps

Here's a basic example of how a Compute Routes call in the Routes API is structured. We'll set 7 Leedon Heights, Singapore as the starting point and Singapore Changi Airport as the destination.

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters,routes.polyline, routes.legs

Request Body

{
	"origin":{
    "location":{
      "latLng":{
        "latitude":1.3144075,
        "longitude":103.8013863
      }
    }
  },
  "destination":{
    "location":{
      "latLng":{
        "latitude":1.3512884,
        "longitude": 103.9762515
      }
    }
  },
	"travelMode": "DRIVE",
	"polylineQuality": "OVERVIEW",
    "routingPreference": "TRAFFIC_AWARE",
	"departureTime": "2025-02-24T15:00:00Z"
}

origin.location.latLng are the coordinates of the route's start location.

destination.location.latLng are the coordinates of the route's end location.

Besides using coordinates, you can also specify locations using Google Place IDs or an address string, like this:

{
    "origin": {
        "address": "7 Leedon Heights, Singapore 267953"
    },
    "destination": {
        "address": "Singapore Changi Airport"
    }
}

travelMode refers to the mode of transport ("DRIVE", "WALK", "TRANSIT" or "BICYCLE"). Right now, truck routing is not supported by the Routes API but this might change in the future.

The polylineQuality setting lets you choose how detailed the route looks on a map. If you go with "HIGH_QUALITY", you'll get a super detailed polyline with lots of points. On the other hand, "OVERVIEW" uses fewer points, making it load much faster but with less precision. As a result, it returns fewer route segments, which means real-time traffic congestion details are less accurate.

The routingPreference parameter has three options: "TRAFFIC_UNAWARE", "TRAFFIC_AWARE", and "TRAFFIC_AWARE_OPTIMAL".

  • Choosing "TRAFFIC_AWARE" enables the routing algorithm to account for real-time traffic conditions when calculating routes. This is ideal for urban areas, where traffic congestion during peak hours can lead to significant delays.
  • Selecting "TRAFFIC_UNAWARE" means the Routes API will base its calculations solely on posted speed limits, without considering live traffic data.

For the most accurate and efficient routes in traffic-heavy areas, "TRAFFIC_AWARE" is the recommended option.

The departureTime field, when used with "routingPreference" : "TRAFFIC_AWARE", is arguably the most critical input parameter. It allows you to specify the start time of the route in RFC3339 UTC "Zulu" format (e.g., "2025-02-24T15:00:00Z"), ensuring that the API accounts for predicted real-time traffic conditions when calculating travel time or drive time. The "Z" at the end of the string signifies Zulu time (GMT+0). To get the correct ETA and travel time for your location, you’ll need to convert it to your local timezone.

If departureTime is left empty, it defaults to the time that the request was made. If you you use a date time value in the past, then the request fails.

The above API call returns the following:

Response

{
    "routes": [
        {
            "legs": [
                {
                    "steps": [
                        {
                            "distanceMeters": 20,
                            "staticDuration": "7s",
                            "polyline": {
                                "encodedPolyline": "_w_G_cqxRJXAF"
                            },
                            "startLocation": {
                                "latLng": {
                                    "latitude": 1.3145563999999998,
                                    "longitude": 103.8035189
                                }
                            },
                            "endLocation": {
                                "latLng": {
                                    "latitude": 1.3145083,
                                    "longitude": 103.8033518
                                }
                            },
                            "navigationInstruction": {
                                "maneuver": "DEPART",
                                "instructions": "Head southwest\nRestricted usage road"
                            },
                            "localizedValues": {
                                "distance": {
                                    "text": "20 m"
                                },
                                "staticDuration": {
                                    "text": "1 min"
                                }
                            },
                            "travelMode": "DRIVE"
                        },
                        // ... 5 more entries
                        {
                            "distanceMeters": 632,
                            "staticDuration": "37s",
                            "polyline": {
                                "encodedPolyline": "ygbGqcsxR}BMmASyAc@kBy@eDqB}Ay@eDyAqDmB"
                            },
                            "startLocation": {
                                "latLng": {
                                    "latitude": 1.3274864,
                                    "longitude": 103.8138518
                                }
                            },
                            "endLocation": {
                                "latLng": {
                                    "latitude": 1.3325178,
                                    "longitude": 103.81634620000001
                                }
                            },
                            "navigationInstruction": {
                                "maneuver": "NAME_CHANGE",
                                "instructions": "Continue onto Adam Rd"
                            },
                            "localizedValues": {
                                "distance": {
                                    "text": "0.6 km"
                                },
                                "staticDuration": {
                                    "text": "1 min"
                                }
                            },
                            "travelMode": "DRIVE"
                        },
                        {
                            "distanceMeters": 19870,
                            "staticDuration": "1010s",
                            "polyline": {
                                "encodedPolyline": "ggcGessxR]CwAm@a@Gg@?YFYXIPE\\BPDRTV^PV@^GXO^e@|C_HrAoC|@aBVKfDkH\\aAf@}ApBoHd@uAhAaCvAuBbBiB`DwCh@y@\\u@XcANmA?oA[oD]yDCiAFy@Ry@n@yBvA{CZeAJy@BoAC}@YaB{EuT_AsEm@eBgA}BiEqIm@gB[qAYoBKwAEiA]wTEgDJuFLkCr@mRDyA@gBPgC\\kKAkBKuAqBcJmCgLKaAEgADsAV}BPy@h@cBlCiH`@aBR{@X_B~@uE\\oAz@}BbA}BbAcBhBaC|CaD~AoAp@a@pLcIzAiAtHoFdAaAn@y@^q@h@}@Zw@h@mBNsAFyFAyAKuASyAK]Q[uCcJ_EgNa@mBOcAMmBM_K@SE_B[wD[wCuDqVkAsI{Fmg@yA{Lk@yCw@wD]oBiC{Lc@eBk@yAMQcCeFcBgDc@eAgE_L{KuZ{BsGeImTcAaDY}@oAuF[gBYmBYmCOoBKwBKcD?mEJaB|@a]NsHHyB@kBIgBIo@Oe@u@gBi@w@eM}OaFgG_GqHiS_W}CwD{@mAsLkO_JuK}AwBwAiCSa@{AmDy@{BWkAS_BQ{BEyC@mAtAmIRaAf@mBv@uBdAuBhBcD`CeCtAiAnBqAvDwBTQnE_CnJmFjDuBxAgAr@o@v@y@vAeBjBqC"
                            },
                            "startLocation": {
                                "latLng": {
                                    "latitude": 1.3325178,
                                    "longitude": 103.81634620000001
                                }
                            },
                            "endLocation": {
                                "latLng": {
                                    "latitude": 1.3405361999999998,
                                    "longitude": 103.9721112
                                }
                            },
                            "navigationInstruction": {
                                "maneuver": "RAMP_LEFT",
                                "instructions": "Merge onto PIE via the ramp to Changi Airport\nToll road"
                            },
                            "localizedValues": {
                                "distance": {
                                    "text": "19.9 km"
                                },
                                "staticDuration": {
                                    "text": "17 mins"
                                }
                            },
                            "travelMode": "DRIVE"
                        },
                        {
                            "distanceMeters": 34,
                            "staticDuration": "2s",
                            "polyline": {
                                "encodedPolyline": "kydGu`ryRFWLa@"
                            },
                            "startLocation": {
                                "latLng": {
                                    "latitude": 1.3405361999999998,
                                    "longitude": 103.9721112
                                }
                            },
                            "endLocation": {
                                "latLng": {
                                    "latitude": 1.3404269,
                                    "longitude": 103.9723952
                                }
                            },
                            "navigationInstruction": {
                                "maneuver": "RAMP_LEFT",
                                "instructions": "Take exit 1"
                            },
                            "localizedValues": {
                                "distance": {
                                    "text": "34 m"
                                },
                                "staticDuration": {
                                    "text": "1 min"
                                }
                            },
                            "travelMode": "DRIVE"
                        }
                    ]
                }
            ],
            "distanceMeters": 22843,
            "duration": "1168s",
            "polyline": {
                "encodedPolyline": "_w_G_cqxRJXAFKFWH@jB|@JXkAlA{FBUeCo@uAm@gAy@}CgDq@{@kCwCcCiCmFkGo@eAk@sAkBgF_@y@_AkAwDiD_@]eAo@{@SaC[}Fq@kDc@_COkK_@}BMmASyAc@kBy@eDqB}Ay@eDyAqDmB]CwAm@a@Gg@?YFYXIPE\\BPDRTV^PV@^GXO^e@|C_HrAoC|@aBVKfDkH\\aAf@}ApBoHd@uAhAaCvAuBbBiB`DwCh@y@\\u@XcANmA?oA[oD]yDCiAFy@Ry@n@yBvA{CZeAJy@BoAC}@YaB{EuT_AsEm@eBgA}BiEqIm@gB[qAYoBKwAEiA]wTEgDJuFLkCr@mRDyA@gBPgC\\kKAkBKuAqBcJmCgLKaAEgADsAV}BPy@h@cBlCiH`@aBR{@X_B~@uE\\oAz@}BbA}BbAcBhBaC|CaD~AoAp@a@pLcIzAiAtHoFdAaAn@y@^q@h@}@Zw@h@mBNsAFyFAyAKuASyAK]Q[uCcJ_EgNa@mBOcAMmBM_K@SE_B[wD[wCuDqVkAsI{Fmg@yA{Lk@yCw@wD]oBiC{Lc@eBk@yAMQcCeFcBgDc@eAgE_L{KuZ{BsGeImTcAaDY}@oAuF[gBYmBYmCOoBKwBKcD?mEJaB|@a]NsHHyB@kBIgBIo@Oe@u@gBi@w@eM}OaFgG_GqHiS_W}CwD{@mAsLkO_JuK}AwBwAiCSa@{AmDy@{BWkAS_BQ{BEyC@mAtAmIRaAf@mBv@uBdAuBhBcD`CeCtAiAnBqAvDwBTQnE_CnJmFjDuBxAgAr@o@v@y@vAeBjBqCFWLa@"
            }
        }
    ]
}

The response from the Routes API is straightforward. The routes object contains one or more route objects, each with a distanceMeters field, representing the total distance traveled, and duration, which is the total time in seconds to complete the route. To determine the ETA, add the duration to the departureTime. For example, if departureTime is "2025-02-24T15:00:00Z" (23:00:00 Singapore time) and the duration is 1,168 seconds, the ETA would be 23:19:28 Singapore time.

Each route consists of one or more legs (if there are multiple waypoints, there will be multiple legs). Each leg contains steps (numbered in the diagram above), which break down the route into manageable segments, usually corresponding to a specific turn, street, or maneuver.

Routes API Pricing

The Google Maps Routes API uses a pay as you go pricing model, with costs determined by the specific features utilized in your request. The Compute Routes endpoint has three pricing tiers  - Basic ($5), Advanced ($10 CPM) and Preferred ($15 CPM). 

Basic: This tier functions similarly to the Directions API, offering fundamental routing from a starting point to a destination. It supports up to 10 intermediate waypoints.

Advanced: Expanding on the basic version, this tier allows routing with 11 to 25 waypoints. It also integrates real-time traffic data and enables additional customization with modifiers such as Side of the roadHeading and Vehicle stopover.

Preferred: The most comprehensive option, this tier includes all features from the previous levels while adding support for Two wheeled vehicle routingToll calculation and Traffic information on polylines.

If you work with a Google Maps Partner, you can access discounted Routes API pricing based on the following price schedule.

0 - 100k 100k - 500k 500k - 1M 1M - 5M 5M - 10M 10M - 20M 20M +
Routes API (Basic) $5.00 $4.00 $3.00 $1.50 $0.38 $0.11 $0.04
Routes API (Advanced) $10.00 $8.00 $6.00 $3.00 $0.75 $0.23 $0.08
Routes API (Preferred) $15.00 $12.00 $9.00 $4.50 $1.14 $0.33 $0.12

Draw a route with real time traffic using the Google Routes API

Hands down, the biggest reason to use the Routes API is its ability to generate traffic aware polylines. The colors on these polylines dynamically adjust based on traffic data, allowing developers to display real time congestion levels on a map.

To get the Routes API to return real time traffic information, you need to first, include routes.polyline and routes.travelAdvisory.speedReadingIntervals in the response field mask.

Second, add the following fields to the request body:

{
    "polylineQuality": "HIGH_QUALITY",
    "extraComputations": "TRAFFIC_ON_POLYLINE",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T01:00:00Z"
}

I previously explained the effects of adding polylineQuality, routingPreference, and departureTime to a Routes API request. Including extraComputations: "TRAFFIC_ON_POLYLINE" means that the response will also include traffic-related information, such as congestion levels along different segments of the route.

Traffic conditions are expressed in terms of speed categories ("NORMAL", "SLOW" and "TRAFFIC_JAM") applicable on a given interval of the response polyline. These intervals are defined by the indexes of their starting (inclusive) and ending (exclusive) polyline points.

Here's an example of a 10 coordinate polyline with three speedReadingIntervals:

{
    "travelAdvisory": {
        "speedReadingIntervals": [
            {
                "startPolylinePointIndex": 0,
                "endPolylinePointIndex": 2,
                "speed": "SLOW"
            },
            {
                "startPolylinePointIndex": 2,
                "endPolylinePointIndex": 4,
                "speed": "NORMAL"
            },
            {
                "startPolylinePointIndex": 4,
                "endPolylinePointIndex": 9,
                "speed": "TRAFFIC_JAM"
            }
        ]
    }
}

The JSON above indicates the following traffic conditions along the route:

  • Coordinates 0 to 2 experience "SLOW" traffic (represented in orange).
  • Coordinates 2 to 4 have "NORMAL" traffic (displayed in blue).
  • Coordinates 4 to 9 face "TRAFFIC_JAM" conditions (marked in red).

But the encoded polyline returned by the Routes API looks something like this:

l@|@JXkA`@mB^eBJg@BU]Ic@Kc@MUGICECGAe@SWMIEe@]a@[[]OOa@c@y@}@UWq@{@s@w@]_@W[KMUUs@u@OQWWg@i@gAqAiAoAu@{@SWQUQYGIOUEKQ]Yu@{eCISQe@Sg@_@y@Yu@Kc@MUGI

How do we convert this into color-coded polylines that we can display on a map? First, decode the polyline provided by the Routes API using the Google Maps Geometry Library.

/* Polyline.jsx */
const Polyline = ({ route, routeIndex, onClick }) => {
  const [polylines, setPolylines] = useState([]);
  const geometryLibrary = useMapsLibrary("geometry");

  useEffect(() => {
    if (!route?.polyline?.encodedPolyline || !geometryLibrary) return;

    const decodePath = (encodedPath) => {
      return geometryLibrary.encoding.decodePath(encodedPath).map((point) => ({
        lat: point.lat(),
        lng: point.lng(),
      }));
    };

    const polylinePoints = decodePath(route.polyline.encodedPolyline);
  });
};

This converts the polyline into an array of individual latitude and longitude coordinates (below).

Next, use the speedReadingIntervals array to determine the startPolylinePointIndex and endPolylinePointIndex for each colored segment. Then extract the corresponding coordinates, group them into segments, and encode each segment into a shorter polyline.

/* Polyline.jsx */

let zIndex = route.travelAdvisory.speedReadingIntervals.length + 2;
const newPolylines = route.travelAdvisory.speedReadingIntervals.map(
  (interval) => {
    const newPoints = polylinePoints.slice(
      interval.startPolylinePointIndex,
      interval.endPolylinePointIndex + 1,
    );

    return {
      path: geometryLibrary.encoding.encodePath(newPoints),
      options: {
        zIndex: 100 - routeIndex + zIndex--,
        strokeColor: SPEED_COLORS[interval.speed] || SPEED_COLORS.NORMAL,
        strokeOpacity: routeIndex === 0 ? 1.0 : 0.5,
        strokeWeight: 6,
      },
    };
  },
);

For each segment, store this polyline in a new array (let's call it polylines) and pair it with an options object that defines its color (based on speed category), opacity, and thickness.

Finally, for each polyline object in polylines, pass the polyline.path and polyline.options object as props into a react-google-maps <Polyline/> component and render it inside a <Map/> component.

/* Polyline.jsx */
import { Polyline as EPolyline } from './external/Polyline';

return (
  <>
    {polylines.map((polyline, index) => (
      <EPolyline
        key={`p-${index}`}
        onClick={(e) => onClick(e, route)}
        encodedPath={polyline.path}
        {...polyline.options}
      />
    ))}
  </>
);

The final output is a series of connected polylines displayed on a map, where each segment represents a different speedReadingInterval.

The complete source code for the code snippets above is available at the routes_api_demo (coming soon!) GitHub repository.

Using the Routes API to plan a route with multiple stops

You can also use the Routes API to add multiple waypoints (intermediate stops) between the origin and the final destination. This is especially useful for planning routes that include scheduled stops, or for avoiding specific areas along the way.

{
    "intermediates": [
        {
            "address": "Marina Bay Sands Singapore, 10 Bayfront Ave, Singapore 018956"
        },
        {
            "address": "Jurong East, Singapore"
        }
    ]
}

Here's a simple example that uses the intermediates array property to add two intermediate waypoints to a Routes API request.

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters, routes.legs

Request

{
    "origin": {
        "address": "7 Leedon Heights, Singapore 267953"
    },
    "destination": {
        "address": "Jewel Changi Airport, Singapore"
    },
    "intermediates": [
        {
            "address": "Marina Bay Sands Singapore, 10 Bayfront Ave, Singapore 018956"
        },
        {
            "address": "Jurong East, Singapore"
        }
    ],
    "travelMode": "DRIVE",
    "polylineQuality": "HIGH_QUALITY",
    "extraComputations": "TRAFFIC_ON_POLYLINE",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T01:00:00Z"
}

Response

{
    "routes": [
        {
            "legs": [
                {
                    // Origin to waypoint 1

                    "distanceMeters": 16972,
                    "duration": "1449s",
                    "staticDuration": "1371s",
                    "polyline": {
                        // ... polyline string
                    },
                    "startLocation": {
                        "latLng": {
                            "latitude": 1.3145563999999998,
                            "longitude": 103.8035189
                        }
                    },
                    "endLocation": {
                        "latLng": {
                            "latitude": 1.2842133,
                            "longitude": 103.8604276
                        }
                    },
                    "steps": [
                        // ... steps array
                    ]
                },
                {
                    // Waypoint 1 to waypoint 2

                    "distanceMeters": 18908,
                    "duration": "1608s",
                    "staticDuration": "1470s",
                    "polyline": {
                        // ... polyline string
                    },
                    "startLocation": {
                        "latLng": {
                            "latitude": 1.2842133,
                            "longitude": 103.8604276
                        }
                    },
                    "endLocation": {
                        "latLng": {
                            "latitude": 1.3327898999999999,
                            "longitude": 103.7437308
                        }
                    },
                    "steps": [
                        // ... steps array
                    ]
                },
                {
                    // Waypoint 2 to destination

                    "distanceMeters": 33931,
                    "duration": "2317s",
                    "staticDuration": "1947s",
                    "polyline": {
                        // ... polyline string
                    },
                    "startLocation": {
                        "latLng": {
                            "latitude": 1.3327898999999999,
                            "longitude": 103.7437308
                        }
                    },
                    "endLocation": {
                        "latLng": {
                            "latitude": 1.3609805,
                            "longitude": 103.99002589999999
                        }
                    },
                    "steps": [
                        // ... steps array
                    ]
                }
            ],
            "distanceMeters": 69811,
            "duration": "4787s"
        }
    ]
}

Each route contains a separate leg for the path from each waypoint to the next. For each additional waypoint added to the route after the origin and destination, the Routes API adds a separate leg.

Metrics for the route as a whole can be found at the end of the response body. In the example above, the total driving distance, distanceMeters, is 69811 m (69.8 km) and the total trip duration, or drive time, is 4787 sec (79 min 47 sec). These values closely match the numbers returned by the Google Maps app.

Optimize waypoints with the Routes API

In the example above, the route sent the driver in a loop around Singapore. By default, the Compute Routes endpoint of the Routes API follows the exact order of the waypoints provided. To optimize the route and arrange stops in the most efficient order, you can include "optimizeWaypointOrder": "true" in the request body.

⚠️
Requests using waypoint optimization are billed at a higher rate ($15 CPM) on the ComputeRoutes-Advanced SKU.

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters, routes

Request

{
    "origin": {
        "address": "7 Leedon Heights, Singapore 267953"
    },
    "destination": {
        "address": "Jewel Changi Airport, Singapore"
    },
    "intermediates": [
        {
            "address": "Marina Bay Sands Singapore, 10 Bayfront Ave, Singapore 018956"
        },
        {
            "address": "Jurong East, Singapore"
        }
    ],
    "travelMode": "DRIVE",
    "polylineQuality": "HIGH_QUALITY",
    "extraComputations": "TRAFFIC_ON_POLYLINE",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T01:00:00Z",
    "optimizeWaypointOrder": "true"
}

Response

{
    "routes": [
        {
            "legs": [
                {
                    "distanceMeters": 13710,
                    "duration": "1322s",
                    "staticDuration": "1247s",
                    "polyline": {
                        "encodedPolyline": "some_polyline"
                    },
                    "startLocation": {
                        "latLng": {
                            "latitude": 1.3145563999999998,
                            "longitude": 103.8035189
                        }
                    },
                    "endLocation": {
                        "latLng": {
                            "latitude": 1.3327525,
                            "longitude": 103.7437052
                        }
                    },
                    "steps": [
                        // ... steps array
                    ],
                    "travelAdvisory": {
                        "speedReadingIntervals": [
                            // ... speedReadingIntervals array
                        ]
                    },
                },
                {
                    "distanceMeters": 18358,
                    "duration": "1700s",
                    "staticDuration": "1331s",
                    "polyline": {
                        some_polylines"
                    },
                    "startLocation": {
                        "latLng": {
                            "latitude": 1.3327525,
                            "longitude": 103.7437052
                        }
                    },
                    "endLocation": {
                        "latLng": {
                            "latitude": 1.2842133,
                            "longitude": 103.8604276
                        }
                    },
                    "steps": [
                        // ... steps array
                    ],
                    "travelAdvisory": {
                        "speedReadingIntervals": [
                            // ... speedReadingIntervals aray
                        ]
                    }
                },
                {
                    "distanceMeters": 19497,
                    "duration": "1204s",
                    "staticDuration": "1125s",
                    "polyline": {
                        "another polyline"
                    },
                    "startLocation": {
                        "latLng": {
                            "latitude": 1.2842133,
                            "longitude": 103.8604276
                        }
                    },
                    "endLocation": {
                        "latLng": {
                            "latitude": 1.3609805,
                            "longitude": 103.99002589999999
                        }
                    },
                    "steps": [
                        // ... steps array
                    ],
                    "travelAdvisory": {
                        "speedReadingIntervals": [
                            // ... speedReadingIntervals array
                        ]
                    },
                }
            ],
            "distanceMeters": 51565,
            "duration": "4227s",
            "staticDuration": "3705s",
            "polyline": {
                "encodedPolyline": "yet_another polyline"
            },
            "description": "PIE",
            "warnings": [
                "This route includes a highway.",
                "This route has tolls."
            ],
            "viewport": {
                "low": {
                    "latitude": 1.2705161999999999,
                    "longitude": 103.7423905
                },
                "high": {
                    "latitude": 1.3613139,
                    "longitude": 103.99001758752786
                }
            },
            "travelAdvisory": {
                "speedReadingIntervals": [
                    ... // speedReadingIntervals array
                ]
            },
            "optimizedIntermediateWaypointIndex": [
                1,
                0
            ],
            "routeToken": "a_route_token",
            "routeLabels": [
                "DEFAULT_ROUTE"
            ],
            "polylineDetails": {}
        }
    ]
}

Using the index numbers for the two waypoints provided in the request, the service then returns the optimized order:

{
    "optimizedIntermediateWaypointIndex": [
        1,
        0
    ]
}

With the waypoint order optimized, the new route now takes 4227 sec (down from 4787 sec) and 51565 m (down from 69811 m).

Google Maps two wheeler routes

The default travel mode in the Routes API is set to "DRIVE", which optimizes routes for passenger cars. However, if you operate a quick commerce business like Swiggy or Getir, where deliveries are made using scooters and motorbikes, you should use the "TWO_WHEELER" option. This ensures that your routes and estimated arrival times (ETAs) are tailored specifically for two wheeled vehicles.

This is especially important if you operate in cities such as Bangkok, Kuala Lumpur or Jakarta, where two wheeled vehicles can weave through traffic and take side streets, and generally reach their destination much faster than a car.

💡
Two wheeler routing is not supported in every country. To see the latest coverage, refer to the Countries and Regions Supported for two wheeled vehicles.

To enable two wheeler mode, add "travelMode" : "TWO_WHEELER" to the request body:

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters

Request

{
    "origin": {
        "address": "Petronas Twin Towers, Kuala Lumpur, Malaysia"
    },
    "destination": {
        "address": "KL Sentral, Kuala Lumpur, Malaysia"
    },
    "travelMode": "TWO_WHEELER",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T01:00:00Z"
}

Response

{
    "routes": [
        {
            "distanceMeters": 7098,
            "duration": "924s"
        }
    ]
}

Using "TWO WHEELER" mode, a trip from Petronas Twin Towers to KL Sentral in Kuala Lumpur takes 924 sec (15m 24s). The same trip on "DRIVE" mode take 1070 sec (17m 50).

When comparing both routes, you'll notice that the "TWO_WHEELER" mode provides a significantly faster travel time. This is not only due to the higher average speeds of two-wheelers but also because, in this specific case, the Routes API prioritizes surface streets. By doing so, it avoids the long detour needed to reach the nearest highway, resulting in a more efficient route.

How to avoid tolls on Google Maps

In last-mile delivery, every cent counts. With tight or nonexistent profit margins, minimizing the cost per delivery is crucial. If you want your drivers to take the most direct route but avoid tolls, you can use the avoidTolls parameter within the routeModifiers object in your request body, like this:

{
    "routeModifiers": {
        "avoidTolls": true
    }
}

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters

Request

{
    "origin": {
        "address": "7 Leedon Heights, Singapore 267953"
    },
    "destination": {
        "address": "Marina Bay Sands Singapore, 10 Bayfront Ave, Singapore 018956"
    },
    "travelMode": "DRIVE",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T00:00:00Z",
    "routeModifiers": {
        "avoidTolls": true
    }
}

Response

{
    "routes": [
        {
            "distanceMeters": 10439,
            "duration": "1683s"
        }
    ]
}

With avoidTolls enabled, the Routes API returns a route that is longer and encounters heavy traffic (28m 3s) compared to one that uses an expressway (23m 5s).

To check the estimated toll cost, add routes.travelAdvisory.tollInfo to the request field mask together with the following extraComputations and routeModifiers (this feature is only available in select cities, check coverage for TollPass).

{
    "extraComputations": [
        "TOLLS"
    ],
    "routeModifiers": {
        "tollPasses": [
            "US_MA_EZPASSMA",
            "US_WA_GOOD_TO_GO"
        ]
    }
}

Show multiple or alternative routes in Google Maps

By default, the Routes API returns a single route, which is typically the fastest route from the origin to the destination. But just like in the Google Maps app, the Routes API can also return alternative routes so that your users can choose one that best fits their requirements. All you need to do is add "computeAlternativeRoutes": true to the request body:

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters,routes.warnings

Request

{
    "origin": {
        "address": "7 Leedon Heights, Singapore 267953"
    },
    "destination": {
        "address": "Marina Bay Sands Singapore, 10 Bayfront Ave, Singapore 018956"
    },
    "computeAlternativeRoutes": true,
    "travelMode": "DRIVE",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T00:00:00Z"
}

Response

{
    "routes": [
        {
            "distanceMeters": 16972,
            "duration": "1377s",
            "warnings": [
                "This route has tolls.",
                "This route includes a highway."
            ]
        },
        {
            "distanceMeters": 10436,
            "duration": "1551s",
            "warnings": [
                "This route has tolls."
            ]
        },
        {
            "distanceMeters": 14351,
            "duration": "1591s",
            "warnings": [
                "This route has tolls.",
                "This route includes a highway."
            ]
        }
    ]
}

In the response, the routes array will include up to three alternative routes, along with polylines, travel distance, trip duration, and any toll or highway usage information.

Vehicle heading and side of road preference

If your chosen origin is next to a two way road, the Routes API will select the fastest route to the destination. This may involve making a right or left turn across the road to optimize travel time.

In certain situations, such as when a taxi driver receives a pickup notification, making a turn across the road may be risky and unsafe, so it's best to avoid it. If you call the Routes API directly, it will return a travel time that is unrealistic because it doesn't take into account the U-turns and additional maneuvers needed to safely get to the other side of the road.

Side of road

To resolve this, add "sideOfRoad": true to the origin object in your Routes API request. This will prompt the routing engine to assume the driver will continue driving until he can safely make a U-turn or take an alternative route to reach their destination. Here's a concrete example of how vehicle headings and side of road preferences work.

Consider the route starting from Hua Yu Wee Seafood Restaurant, 462 Upper E Coast Rd, Singapore 466508 to Villa Haji Kahar, 3 Bedok Ave, Singapore 469898.

In Singapore, cars drive on the left side of the road. If you start from Hua Yu Wee, the default route from the Routes API requires a risky right turn into traffic. To get a safer and more realistic route, specify that the driver should continue on the same side of the road he is already on.

Endpoint POST https://routes.googleapis.com/directions/v2:computeRoutes

Headers
Content-Type: application/json
X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: routes.duration,routes.distanceMeters,routes.polyline

Request

{
    "origin": {
        "address": "Hua Yu Wee Seafood Restaurant, 462 Upper E Coast Rd, Singapore 466508",
        "sideOfRoad": true
        
    },
    "destination": {
        "address": "Villa Haji Kahar, 3 Bedok Ave, Singapore 469898"
    },
    "computeAlternativeRoutes": true,
    "travelMode": "DRIVE",
    "routingPreference": "TRAFFIC_AWARE",
    "departureTime": "2025-02-24T00:00:00Z"
}

Response

{
    "routes": [
        {
            "distanceMeters": 2506,
            "duration": "494s",
            "polyline": {
                "encodedPolyline": "az_GcalyRb@lFoKfA[ASO}@}B]eAa@m@{@cACK?wEj@CNGVs@LiAPIjAG~FQ`@EESy@wAGSIIa@wAg@aCYiBoBeHq@mCk@_FaBiIWcAu@gEKm@]mBUw@iA}Cg@NkBlAoAkBiAfA"
            }
        }
    ]
}

With sideOfRoad set to true, the new route takes 8m 14s compared to the 4m 27s for the default route provided by the Routes API.

Vehicle heading

Alternatively, if you know the driver’s heading, for example, by retrieving it from the location listener in the Google Navigation SDK or by calculating it from stored GPS traces using the Compute Heading method in the Google Geometry library - you can include the heading field in the origin object to achieve a similar result.

This is especially useful when using the driver's real-time GPS location as the origin, as it may not always align with the correct side of the road they are driving on.

Heading values are whole numbers that align with the compass directions, and therefore range from zero to 359. For example, a value of 0 indicates a heading direction of due North. In the example above, the heading of a vehicle driving on the left side of the road and picking someone ip from Hua Yu Wee restaurant is approximately 261.18.

{
    "origin": {
        "location": {
            "latLng": {
                "latitude": 1.315003,
                "longitude": 103.941463
            },
            "heading": 261
        }
    },
    ...
}

Routes API alternatives

There are three main alternatives to the Routes API. The Google Directions API, the OSRM Route API, and competing navigation and location based services such as MapBox, HERE Maps and TomTom.

Google Directions API The Google Directions API is the OG point to point routing API. It's less expensive (pricing starts at $5 CPM) but does not include traffic on polylines, toll information or two wheeler routes.

OSRM Route API The Open Source Routing Machine (OSRM) Route API uses user contributed OpenStreetMap data to offer a free alternative to the Google Maps Routes API. Its main drawback is that it does not account for real time traffic, which can lead to inaccurate ETAs and travel times. It also requires some engineering expertise to install and set up correctly.

MapBox, HERE Maps and TomTom MapBox, HERE Maps and TomTom are geospatial technology and mapping companies that primarily serve governments, auto manufacturers, and large enterprises. Their APIs are generally more affordable than Google Maps but can be more complex to use. HERE Maps and TomTom stand out for their built in truck routing features, making them a strong choice for companies operating large delivery trucks or heavy vehicles.

Using the Routes API to build a Google Maps clone

In this final section, we'll create a Google Maps clone. The app will allow users to select a start and end location and will retrieve one or more routes for both cars and two wheelers using the Routes API. The app will also show real time traffic conditions using color coded polylines, making it easy to see traffic conditions along the route.

How our code is structured

routes_api_demo (coming soon!) is a single page react app built on three main components:

  • <SearchBox/>, a container for the <PlaceAutocompleteInput/> input fields where we'll enter the origin and destination for our route,
  • <Map/>, which renders and manages a Google Map instance for us to draw traffic aware route polylines on.
  • A thin middleware layer used to make call to the Google Routes API

Like our other tutorials, we'll use @vis.gl/react-google-maps  to quickly set up our app. This library simplifies integrating Google Maps into a React app by offering prebuilt components that make it easy to handle map interactions and UI in a React-friendly way. You can find the complete source code and installation instructions for running the app locally in our GitHub repository (coming soon!).

App.jsx

App.jsx functions as the primary entry point to our app. It features a streamlined, map-focused interface with a search box (<SearchBoxContainer/>) on the left, where users can enter their origin and destination to plan a route. The Google Maps base map (<MapContainer/>) serves as the background on which the route polyline is displayed.

/* App.jsx */
import React from 'react';
import { APIProvider } from '@vis.gl/react-google-maps';
import MapContainer from './containers/MapContainer';
import SearchBoxContainer from './containers/SearchBoxContainer';

import './App.scss';

const App = () => {
  return (
    <div className="App">
      <APIProvider apiKey={process.env.REACT_APP_GOOGLE_API_KEY}>
        <div className="control-pannel">
          <SearchBoxContainer />
        </div>
        <MapContainer />
      </APIProvider>
    </div>
  );
};

export default App;

The <APIProvider/> wrapper component supplies the Google Maps API context to it's <MapContainer/> child component. This setup allows <MapContainer/> to access the Google Maps API using the REACT_APP_GOOGLE_API_KEY.

SearchBoxContainer.jsx

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

SearchBox.jsx

sswwe

/* SearchBox.jsx */
import React from 'react';
import PlaceAutocompleteInput from './PlaceAutocompleteInput';
import { TRAVEL_MODES } from '../constants';
import { formatDuration } from '../services/time';
import Image from '../images';

import './SearchBox.scss';

const SearchBox = ({ routes, travelMode, onTravelModeChange, onOriginAddressChange, onDestinationAddressChange }) => {
  return (
    <div className="search-box">
      <div className="travel-mode-filter-btns">
        {TRAVEL_MODES.map((mode) => {
          const duration = formatDuration(routes[mode]?.[0]?.duration);
          return (
            <button key={mode} className={travelMode === mode ? 'active' : ''} onClick={() => onTravelModeChange(mode)}>
              <Image type={mode} />
              {duration && <span>{duration}</span>}
            </button>
          );
        })}
      </div>
      <div className="input-address-group">
        <Image type="ThreeDot" />
        <PlaceAutocompleteInput onPlaceSelect={onOriginAddressChange} />
        <PlaceAutocompleteInput onPlaceSelect={onDestinationAddressChange} />
      </div>
    </div>
  );
};

export default SearchBox;

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

  • [ x ] Whats is the Routes API?
  • [ x ] Routes API field masks
  • [ x ] Routes API example
  • [ x ] Routes API pricing
  • [ x ] Draw a route with real time traffic and polylines
  • [ x ] Plan a route with multiple stops
  • [ x ] Two wheeler routes
  • [ x ] Avoid tolls
  • [ x] Multiple or alternative routes
  • [ x ] Vehicle heading and side of road preference
  • [ x ] Routes API alternatives
  • [ ] Building a navigation app using the Routes API

how to avoid tolls on google maps

google travel time api

google route finder

google maps api directions multiple routes

route planning api

multi stop route optimization system

google driving distance api

google maps api distance between two points

does maps account for traffic

google maps drive time api

how to optimize route in google maps

route optimization google maps

making a route in google maps

google maps api estimated travel time

google maps multiple stops

plan a route with multiple stops

plan a route with multiple stops

google maps distance matrix

google route maps directions

google maps api distance matrix

google api distance matrix specific time

google map distance matrix api

google's distance matrix api

google matrix api

google distance matrix departure time

google api pricing matrix

google maps api time to destination

---

google maps to avoid tolls

create route google maps api

google maps api draw route

google maps api draw route between multiple points

google maps api optimize route

google maps api route between two points

google maps api shortest route multiple destinations

google route api example

google routes api pricing

google routing api pricing

routes api google

routes api google maps

google api optimize route

google maps api route distance

google maps api route example

google maps draw route api

google maps get route api

google maps route api

google maps route api example