Some time ago, we published a full-featured example that shows how to build printable route directions using the Geoapify Routing API:
👉 Printable route directions example
The example combines multiple features in one project: route calculation, turn-by-turn instructions, static maps, elevation profiles, and a print-friendly layout. This makes it useful as a reference, but not easy to understand or reuse piece by piece.
Instead of covering everything at once, we are breaking that example into a series of focused tutorials. Each article explains one part of the solution and the data behind it, using plain JavaScript and minimal UI.
This is the first tutorial in the series. It focuses on the basics:
- requesting a route from the Geoapify Routing API
- understanding the routing response structure
- extracting turn-by-turn navigation instructions
- Try it in the live demo: View on CodePen
Later tutorials will reuse this foundation to add maps, previews, elevation data, and printable layouts.
Table of Contents
- What are Routing Directions and how to get them
- Build the Routing API Request
- Fetch and Parse the Response
- Display Route Summary
- Show Turn-by-Turn Directions
- Explore the Demo
- Summary
- FAQ
Routing directions: what they are and how the API provides them
Routing directions are the step-by-step instructions that guide a user along a route. They are the human-readable part of navigation, not just a line on a map.
Typical directions look like:
- “Head north on Baker Street”
- “In 350 m, turn right onto Oxford Street”
- “Continue for 1.2 km”
- “You have arrived at your destination”
These instructions are used in navigation panels, delivery apps, and printable route sheets where users need clear guidance from point A to point B.
When a route is calculated with the Geoapify Routing API, the response already contains these routing directions as structured steps, including distance, duration, and instruction text.
In this tutorial, we focus on obtaining this data and understanding where routing directions are located in the API response. In later parts of the series, we will show how to display, format, and visualize them in different ways.
Step 1: Build the Routing API Request
The Geoapify Routing API calculates routes between waypoints. You provide coordinates as latitude/longitude pairs, choose a travel mode, and request details like turn-by-turn instructions.
Define waypoints and parameters
Start by setting up the API key and waypoints:
const apiKey = "YOUR_API_KEY";
// Waypoints: Munich to Berlin
// Format: lat,lon|lat,lon|...
const waypoints = "48.1351,11.5820|52.5200,13.4050";
🔑 API Key: Please sign up at geoapify.com and generate your own API key.
Build the request URL
Construct the full API request with waypoints, mode, and details:
const routingUrl = `https://api.geoapify.com/v1/routing?waypoints=${waypoints}&mode=drive&details=instruction_details&apiKey=${apiKey}`;
Key parameters
| Parameter | Description | Example |
|---|---|---|
waypoints |
Pipe-separated lat,lon pairs |
48.13,11.58 | 52.52,13.40
|
mode |
Travel mode |
drive, walk, bicycle, truck
|
details |
Include extra data |
instruction_details for turn-by-turn |
apiKey |
Your Geoapify API key | Get one at myprojects.geoapify.com |
The details=instruction_details parameter tells the API to include turn-by-turn navigation instructions in the response.
📘 API Documentation: Learn more about request parameters in the Routing API docs.
Step 2: Fetch and Parse the Response
Now we'll request the route from the API and extract the route data.
Make the API request
async function fetchRoute() {
try {
const response = await fetch(routingUrl);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
// Check if we got a valid route
if (!data.features || data.features.length === 0) {
throw new Error("No route found");
}
// The route is the first feature in the GeoJSON response
const route = data.features[0];
// Display the results
displaySummary(route);
displayDirections(route);
displayRawResponse(data);
} catch (error) {
console.error("Error fetching route:", error);
}
}
The API returns a GeoJSON FeatureCollection. The route geometry and all metadata are in the first feature.
Response structure
The response contains everything you need for navigation:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"mode": "drive",
"waypoints": [
{ "location": [11.582, 48.1351], "original_index": 0 },
{ "location": [13.405, 52.52], "original_index": 1 }
],
"units": "metric",
"details": ["instruction_details"],
"distance": 585050, // Total distance in meters
"distance_units": "meters",
"time": 20454.257, // Total time in seconds
"legs": [...] // Route segments with steps
},
"geometry": {
"type": "MultiLineString",
"coordinates": [...] // Route line coordinates
}
}]
}
The route data is in features[0].properties:
| Field | Description |
|---|---|
distance |
Total route distance in meters |
distance_units |
Units for distance (meters or miles) |
time |
Estimated travel time in seconds |
legs |
Array of route segments (one per waypoint pair) |
waypoints |
Waypoints with coordinates and original order |
mode |
Travel mode used (drive, walk, bicycle, etc.) |
units |
Measurement system (metric or imperial) |
ℹ️ Units and language
Distance values are returned in meters and time in seconds by default. The measurement system depends on the
unitsparameter (metricorimperial).Turn-by-turn instruction text is localized. Use the
langparameter in the request (for example,lang=de) to receive instructions in the required language.
Step 3: Display Route Summary
Extract and format the route summary data for display.
Format distance and time
function displaySummary(route) {
const props = route.properties;
// Extract key information
const distance = props.distance; // in meters
const time = props.time; // in seconds
const legs = props.legs ? props.legs.length : 0;
// Format distance
const distanceKm = (distance / 1000).toFixed(1);
const distanceMiles = (distance / 1609.34).toFixed(1);
// Format time
const hours = Math.floor(time / 3600);
const minutes = Math.floor((time % 3600) / 60);
const timeFormatted = hours > 0 ? `${hours}h ${minutes}min` : `${minutes}min`;
// Display in UI (HTML generation omitted for brevity)
}
The demo shows total distance in both kilometers and miles, estimated travel time, number of route legs, and travel mode.
The route summary displays total distance, travel time, and route details extracted from the API response.
Step 4: Show Turn-by-Turn Directions
Each route contains legs (segments between waypoints), and each leg contains steps (individual maneuvers with instructions).
Extract and display steps
function displayDirections(route) {
const props = route.properties;
if (!props.legs || props.legs.length === 0) {
return;
}
let stepNumber = 1;
// Loop through each leg (segment between waypoints)
props.legs.forEach((leg) => {
if (!leg.steps) return;
// Loop through each step in the leg
leg.steps.forEach((step) => {
const instruction = step.instruction;
const distance = step.distance;
const time = step.time;
// Format step distance
let distanceText = "";
if (distance >= 1000) {
distanceText = `${(distance / 1000).toFixed(1)} km`;
} else {
distanceText = `${Math.round(distance)} m`;
}
// Format step time
let timeText = "";
if (time >= 60) {
timeText = `${Math.round(time / 60)} min`;
} else {
timeText = `${Math.round(time)} sec`;
}
// Display step (HTML generation omitted)
console.log(`${stepNumber}. ${instruction.text} - ${distanceText}`);
stepNumber++;
});
});
}
Each step includes:
| Field | Description |
|---|---|
instruction.text |
Human-readable turn instruction |
instruction.type |
Maneuver type (e.g., TurnRight, TurnLeft) |
distance |
Distance for this step in meters |
time |
Time estimate for this step in seconds |
💡 Implementation note
Use
instruction.typefor logic and UI decisions (icons, grouping, behavior). These values are stable and language-independent.The
instruction.textfield is localized and intended for display only. Do not rely on it for application logic.
Instruction types
The instruction.type field tells you what kind of maneuver it is. Here are some common types:
| Type | Description |
|---|---|
StartAt, StartAtRight, StartAtLeft
|
Starting point |
DestinationReached, DestinationReachedRight, DestinationReachedLeft
|
Arrival at destination |
Straight |
Continue straight |
Right, SlightRight, SharpRight
|
Right turns (varying angles) |
Left, SlightLeft, SharpLeft
|
Left turns (varying angles) |
Roundabout |
Enter roundabout |
ExitRight, ExitLeft
|
Take an exit |
Merge, MergeRight, MergeLeft
|
Merge onto another road |
FerryEnter, FerryExit
|
Ferry crossing |
You can use these types to display appropriate turn icons or customize the UI presentation.
📘 Full list: See all instruction types in the Routing API documentation.
Turn-by-turn directions list each step with instruction text, distance, and estimated time.
Step 5: Explore the Demo
This demo intentionally avoids map rendering. Its purpose is to show how routing data and turn-by-turn directions can be requested, parsed, and displayed without any mapping library.
The live CodePen demo ties everything together in a working example.
What the demo shows
- Fetch Route - Click the button to request a route between Munich and Berlin
- Route Summary - Total distance, time, and number of legs
- Turn-by-Turn Directions - Each step with instruction text, distance, and time
- Raw API Response - Simplified view of the JSON structure
Experiment with the code
- Change the
waypointsvariable to different cities - Update the
modeparameter towalk,bicycle, ortruck - Add more waypoints using pipe-separated coordinates (e.g.,
"lat1,lon1|lat2,lon2|lat3,lon3")
Summary
We’ve built a simple but complete routing workflow that focuses on working with routing data, not UI frameworks:
- Build a routing request by defining waypoints, travel mode, and required details
- Request routing data and parse the GeoJSON response
- Extract route-level properties such as total distance and travel time
- Process turn-by-turn directions as structured steps that can be rendered, logged, or stored
This approach gives you a reusable foundation for:
- navigation panels and instruction lists,
- delivery and field-service applications,
- server-side route processing and report generation.
An important practical point: treat routing directions as data, not as final UI. Keep them separate from presentation logic so you can reuse the same response for web, mobile, or printable outputs without recalculating the route.
Useful links:
FAQ
Q: How many waypoints can I include in a route?
A: The Routing API supports multiple waypoints. For complex routes with many stops, consider the Route Planner API which optimizes the order of visits.
Q: What travel modes are available?
A: The API supports drive, walk, bicycle, bus, truck, scooter, motorcycle, and specialized modes. Each mode uses appropriate roads and calculates realistic travel times. Check the Routing API documentation for the complete list.
Q: Can I get elevation data with the route?
A: Yes. Add details=elevation to the request. The response will include elevation_range data in each leg.
Q: How do I avoid highways or tolls?
A: Add the avoid parameter: avoid=highways or avoid=tolls. You can combine multiple: avoid=highways|tolls|ferries.
Q: Can I get the route in a different language?
A: Yes, add the lang parameter (e.g., lang=de for German). The instruction text will be localized.
Try It Now
Please sign up at geoapify.com and generate your own API key to start building route-based applications.


Top comments (0)