Turn-by-turn directions, multi-stop routing, and route optimization using the MapQuest Directions API. Covers route types, narrative maneuvers, response parsing, and common pitfalls.
The MapQuest Directions API provides turn-by-turn directions between two or more locations. Base URL: https://www.mapquestapi.com/directions/v2
Choose the right routeType for the use case:
| routeType | Description | Use When |
|---|---|---|
fastest | Minimize travel time (default) | Standard driving |
shortest | Minimize distance | Fuel efficiency priority |
pedestrian | Walking paths | Walking directions |
bicycle | Bike-friendly routes | Cycling apps |
multimodal |
| Mix of transit + walking |
| Urban transit apps |
async function getDirections(from, to, apiKey, options = {}) {
const params = new URLSearchParams({
key: apiKey,
from,
to,
routeType: options.routeType || 'fastest',
unit: options.unit || 'm', // 'm' = miles, 'k' = kilometers
narrativeType: 'text', // 'text', 'microformat', 'none'
enhancedNarrative: false,
avoidTimedConditions: false,
});
const response = await fetch(
`https://www.mapquestapi.com/directions/v2/route?${params}`
);
const data = await response.json();
if (data.info.statuscode !== 0) {
throw new Error(`Directions error: ${data.info.messages.join(', ')}`);
}
return data.route;
}
For routes with 3+ stops, use the from/to + multiple to parameters, or POST with a locations array:
async function getMultiStopRoute(locations, apiKey) {
// locations: array of address strings or {lat, lng} objects
const response = await fetch(
`https://www.mapquestapi.com/directions/v2/route?key=${apiKey}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
locations: locations,
options: {
routeType: 'fastest',
unit: 'm',
narrativeType: 'text',
avoids: [], // e.g. ['Toll Road', 'Ferry', 'Unpaved']
doReverseGeocode: false, // Skip reverse geocoding stops (faster)
}
})
}
);
const data = await response.json();
return data.route;
}
// Example with lat/lng objects (avoid geocoding overhead)
const stops = [
{ latLng: { lat: 34.0522, lng: -118.2437 } }, // Los Angeles
{ latLng: { lat: 36.1699, lng: -115.1398 } }, // Las Vegas
{ latLng: { lat: 33.4484, lng: -112.0740 } }, // Phoenix
];
function parseRoute(route) {
return {
// Overall trip summary
distance: route.distance, // in chosen unit (miles or km)
time: route.time, // seconds
formattedTime: route.formattedTime, // 'h:mm:ss'
// Route shape (encoded polyline)
shape: route.shape?.shapePoints, // flat array [lat, lng, lat, lng, ...]
// Per-leg details (one leg per stop-to-stop segment)
legs: route.legs.map(leg => ({
distance: leg.distance,
time: leg.time,
maneuvers: leg.maneuvers.map(m => ({
narrative: m.narrative, // Human-readable instruction
distance: m.distance,
time: m.time,
turnType: m.turnType, // 0=straight, 1=slight right, etc.
startPoint: m.startPoint, // {lat, lng}
signs: m.signs, // Road signs
iconUrl: m.iconUrl,
}))
})),
// Bounding box for fitting map view
boundingBox: route.boundingBox, // {ul: {lat,lng}, lr: {lat,lng}}
// Status
hasTollRoad: route.hasTollRoad,
hasHighway: route.hasHighway,
hasFerry: route.hasFerry,
};
}
const avoidOptions = [
'Toll Road',
'Limited Access',
'Ferry',
'Unpaved',
'Seasonal Closure',
'Country Border Crossing',
];
// In request options: