At Routific, we’re obsessed with helping your team optimize delivery routes. But we know that you’ve already put a lot of work into customizing a system that works for your business. That’s where Routific’s Engine API comes in.

With Routific's Engine API, you can integrate our proprietary algorithms into your software. Countless businesses benefit from route optimization. Here's just a sample of some of the most popular use-cases:

  • meal delivery
  • grocery delivery
  • parcel delivery
  • field services
  • moving companies
  • moving boxes
  • storage solutions

All these businesses have something in common – the need to plan daily routes to visit multiple stops, either with a single vehicle or a fleet of vehicles.

In this article, we’ll walk you through how to get started with Routific’s Engine API in some of the most common routing scenarios. Let’s get started.

Getting Started

For this guide, all you need is your favourite coding editor and a language of your choice. We're going to provide examples in JavaScript and Go. Here's some boilerplate code that you can use as a base if you end up following along with either of those languages.

// Allows us to make HTTP requests. We'll use Axios
const axios = require('axios');
// You can get this token when you sign up for an account with our API!
const API_TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1MzEzZDZiYTNiMDBkMzA4MDA2ZTliOGEiLCJpYXQiOjEzOTM4MDkwODJ9.PR5qTHsqPogeIIe0NyH2oheaGR-SJXDsxPTcUQNq90E';
const routingInfo = YOUR_INPUT;
const url = 'https://api.routific.com/v1/vrp';
axios.post(url, routingInfo, { headers: { Authorization: `Bearer ${API_TOKEN}` } })
  .then((response) => {
    console.log('This is my response!');
    console.log(response);
  })
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"
)

var url = "https://api.routific.com/v1/vrp"
var token string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1MzEzZDZiYTNiMDBkMzA4MDA2ZTliOGEiLCJpYXQiOjEzOTM4MDkwODJ9.PR5qTHsqPogeIIe0NyH2oheaGR-SJXDsxPTcUQNq90E"
var input string = YOUR_INPUT

func main() {
	client := &http.Client{
		Timeout: 5 * time.Second,
	}
	req, err := http.NewRequest("POST", url, strings.NewReader(input))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "bearer "+token)

	res, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s", &body)
}

In the following documentation, we will introduce some concepts such as durations, shift times and time windows in different scenarios. To play with it, all you have to do is to change the input (YOUR_INPUT in the code), log the output and we will explain what they mean in this article.

Quick start with common delivery scenarios

Scenario 1: No constraints

In the first scenario, you will be visiting every stop, with no other constraints to account for.

You would need to send our API the addresses of your visits and your fleet. We will return the optimal allocation and order in which you should visit them. Simple and straightforward!

Here’s what the code would look like:

// Allows us to make HTTP requests. We'll use Axios
const axios = require('axios');
// You can get this token when you sign up for an account with our API!
const API_TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1MzEzZDZiYTNiMDBkMzA4MDA2ZTliOGEiLCJpYXQiOjEzOTM4MDkwODJ9.PR5qTHsqPogeIIe0NyH2oheaGR-SJXDsxPTcUQNq90E';
const routingInfo = {
  "visits": {
    "coco_and_tengtengs": {
      "location": {
        "lat": 49.280231,
        "lng": -123.095983
      }
    },
    "sugar_and_spice": {
      "location": {
        "lat": 49.280232,
        "lng": -123.054692
      }
    },
    "jimmy_pestos": {
      "location": {
        "lat": 49.268199,
        "lng": -123.003409
      }
    },
    "tiny_gyoza": {
      "location": {
        "lat": 49.215390,
        "lng": -123.091364
      }
    },
    "finnegans": {
      "location": {
        "lat": 49.263801,
        "lng": -123.141087
      }
    }
  },
  "fleet": {
    "vehicle_1": {
      "start_location": {
        "lat": 49.2847463,
        "lng": -123.1123453
      },
      "end_location": {
        "lat": 49.2847463,
        "lng": -123.1123453
      }
    }
  }
};
const url = 'https://api.routific.com/v1/vrp';

axios.post(url, routingInfo, { headers: { Authorization: `Bearer ${API_TOKEN}` } })
  .then((response) => {
    console.log('This is my response!');
    console.log(response);
  })
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"
)

var url = "https://api.routific.com/v1/vrp"
var token string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1MzEzZDZiYTNiMDBkMzA4MDA2ZTliOGEiLCJpYXQiOjEzOTM4MDkwODJ9.PR5qTHsqPogeIIe0NyH2oheaGR-SJXDsxPTcUQNq90E"
var input string = `
{
  "visits": {
    "coco_and_tengtengs": {
      "location": {
        "lat": 49.280231,
        "lng": -123.095983
      }
    },
    "sugar_and_spice": {
      "location": {
        "lat": 49.280232,
        "lng": -123.054692
      }
    },
    "jimmy_pestos": {
      "location": {
        "lat": 49.268199,
        "lng": -123.003409
      }
    },
    "tiny_gyoza": {
      "location": {
        "lat": 49.215390,
        "lng": -123.091364
      }
    },
    "finnegans": {
      "location": {
        "lat": 49.263801,
        "lng": -123.141087
      }
    }
  },
  "fleet": {
    "vehicle_1": {
      "start_location": {
        "lat": 49.2847463,
        "lng": -123.1123453
      },
      "end_location": {
        "lat": 49.2847463,
        "lng": -123.1123453
      }
    }
  }
}
`

func main() {
	client := &http.Client{
		Timeout: 5 * time.Second,
	}
	req, err := http.NewRequest("POST", url, strings.NewReader(input))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "bearer "+token)

	res, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s", &body)
}

Let's start by defining a few things for you. For the input, there are 2 sections here: visits and fleet.

visits is an object that contains all the visits a route need to deliver to, while fleet consists of your drivers.

In our example above, each visit only has one field: location which consists of lat and lng to indicate the latitude and longitude of the location. For each vehicle in the fleet, we have 2 location objects: start_location and end_location to specify where the vehicle starts and ends.

You’ll notice in this example, that we are using LAT/LONG coordinates. If you don’t happen to have those, you can also provide an address in the location object. It would look something like this:

"location": { address: "555 W Hastings Street, Vancouver, BC V6B4N6, Canada" }

If everything goes well, you should be able to see the result:

{
    "total_travel_time": 70,
    "total_idle_time": 0,
    "total_visit_lateness": 0,
    "total_vehicle_overtime": 0,
    "vehicle_overtime": {
        "vehicle_1": 0
    },
    "total_break_time": 0,
    "num_unserved": 0,
    "unserved": null,
    "solution": {
        "vehicle_1": [
            {
                "location_id": "vehicle_1_start",
                "location_name": "vehicle_1_start"
            },
            {
                "location_id": "coco_and_tengtengs",
                "location_name": "coco_and_tengtengs"
            },
            {
                "location_id": "sugar_and_spice",
                "location_name": "sugar_and_spice"
            },
            {
                "location_id": "jimmy_pestos",
                "location_name": "jimmy_pestos"
            },
            {
                "location_id": "tiny_gyoza",
                "location_name": "tiny_gyoza"
            },
            {
                "location_id": "finnegans",
                "location_name": "finnegans"
            },
            {
                "location_id": "vehicle_1_end",
                "location_name": "vehicle_1_end"
            }
        ]
    },
    "total_working_time": 70,
    "status": "success",
    "num_late_visits": 0
}

From the solution of the response, we can see the permutation of the visits which tells us the sequence we should deliver to those locations. You'll also notice some other information in the response such as total_travel_time and total_working_time.

You can find more details of them under Output section on the left panel.

Scenario 2: Duration

We have the permutation in the solution above, but we have no idea how long will it take from this location to another location, or when to arrive a location and when to depart for another. This is where Duration comes in.

We can set a Duration for each visit to specify how many minutes the visit will take. When we set some durations for our input and it will look like this:

{
    "visits": {
        "coco_and_tengtengs": {
            "duration": 10,
            "location": {
                "lat": 49.280231,
                "lng": -123.095983
            }
        },
        "sugar_and_spice": {
            "duration": 10,
            "location": {
                "lat": 49.280232,
                "lng": -123.054692
            }
        },
        "jimmy_pestos": {
            "duration": 10,
            "location": {
                "lat": 49.268199,
                "lng": -123.003409
            }
        },
        "tiny_gyoza": {
            "duration": 15,
            "location": {
                "lat": 49.215390,
                "lng": -123.091364
            }
        },
        "finnegans": {
            "duration": 10,
            "location": {
                "lat": 49.263801,
                "lng": -123.141087
            }
        }
    },
    "fleet": {
        "vehicle_1": {
            "start_location": {
                "lat": 49.2847463,
                "lng": -123.1123453
            },
            "end_location": {
                "lat": 49.2847463,
                "lng": -123.1123453
            }
        }
    }
}

After we send the request once more, we will have a response that looks like this:

{
    "total_travel_time": 70,
    "total_idle_time": 0,
    "total_visit_lateness": 0,
    "total_vehicle_overtime": 0,
    "vehicle_overtime": {
        "vehicle_1": 0
    },
    "total_break_time": 0,
    "num_unserved": 0,
    "unserved": null,
    "solution": {
        "vehicle_1": [
            {
                "location_id": "vehicle_1_start",
                "location_name": "vehicle_1_start",
                "arrival_time": "00:00"
            },
            {
                "location_id": "coco_and_tengtengs",
                "location_name": "coco_and_tengtengs",
                "arrival_time": "00:05",
                "finish_time": "00:15"
            },
            {
                "location_id": "sugar_and_spice",
                "location_name": "sugar_and_spice",
                "arrival_time": "00:24",
                "finish_time": "00:34"
            },
            {
                "location_id": "jimmy_pestos",
                "location_name": "jimmy_pestos",
                "arrival_time": "00:43",
                "finish_time": "00:53"
            },
            {
                "location_id": "tiny_gyoza",
                "location_name": "tiny_gyoza",
                "arrival_time": "01:13",
                "finish_time": "01:28"
            },
            {
                "location_id": "finnegans",
                "location_name": "finnegans",
                "arrival_time": "01:47",
                "finish_time": "01:57"
            },
            {
                "location_id": "vehicle_1_end",
                "location_name": "vehicle_1_end",
                "arrival_time": "02:05"
            }
        ]
    },
    "total_working_time": 125,
    "status": "success",
    "num_late_visits": 0
}

From the response we can see when a driver will arrive at a visit, how long will it stay (duration) and when to leave (finish_time).

Scenario 3: Shift times

But there’s a small problem here. All of the routes start at 00:00, which is in the middle of the night. To fix that, we need to introduce shift times.

In this scenario, your fleet of drivers has different shifts that must be taken into account when planning routes. For this example, let’s add a shift time of 09:00 to 17:00 (or 9AM to 5 PM):

{
    "visits": {
        "coco_and_tengtengs": {
            "duration": 10,
            "location": {
                "lat": 49.280231,
                "lng": -123.095983
            }
        },
        "sugar_and_spice": {
            "duration": 10,
            "location": {
                "lat": 49.280232,
                "lng": -123.054692
            }
        },
        "jimmy_pestos": {
            "duration": 10,
            "location": {
                "lat": 49.268199,
                "lng": -123.003409
            }
        },
        "tiny_gyoza": {
            "duration": 15,
            "location": {
                "lat": 49.215390,
                "lng": -123.091364
            }
        },
        "finnegans": {
            "duration": 10,
            "location": {
                "lat": 49.263801,
                "lng": -123.141087
            }
        }
    },
    "fleet": {
        "vehicle_1": {
            "shift_start": "9:00",
            "shift_end": "17:00",
            "start_location": {
                "lat": 49.2847463,
                "lng": -123.1123453
            },
            "end_location": {
                "lat": 49.2847463,
                "lng": -123.1123453
            }
        }
    }
}

The added shift times are reflected in the code here:

 "shift_start": "9:00",     
 "shift_end": "17:00"

and here is the response:

{
    "total_travel_time": 70,
    "total_idle_time": 0,
    "total_visit_lateness": 0,
    "total_vehicle_overtime": 0,
    "vehicle_overtime": {
        "vehicle_1": 0
    },
    "total_break_time": 0,
    "num_unserved": 0,
    "unserved": null,
    "solution": {
        "vehicle_1": [
            {
                "location_id": "vehicle_1_start",
                "location_name": "vehicle_1_start",
                "arrival_time": "09:00"
            },
            {
                "location_id": "coco_and_tengtengs",
                "location_name": "coco_and_tengtengs",
                "arrival_time": "09:05",
                "finish_time": "09:15"
            },
            {
                "location_id": "sugar_and_spice",
                "location_name": "sugar_and_spice",
                "arrival_time": "09:24",
                "finish_time": "09:34"
            },
            {
                "location_id": "jimmy_pestos",
                "location_name": "jimmy_pestos",
                "arrival_time": "09:43",
                "finish_time": "09:53"
            },
            {
                "location_id": "tiny_gyoza",
                "location_name": "tiny_gyoza",
                "arrival_time": "10:13",
                "finish_time": "10:28"
            },
            {
                "location_id": "finnegans",
                "location_name": "finnegans",
                "arrival_time": "10:47",
                "finish_time": "10:57"
            },
            {
                "location_id": "vehicle_1_end",
                "location_name": "vehicle_1_end",
                "arrival_time": "11:05"
            }
        ]
    },
    "total_working_time": 125,
    "status": "success",
    "num_late_visits": 0
}

Now things are looking a bit cleaner. Your routes are being planned using Routific’s algorithm, and your drivers’ shifts are starting on time. But what do you do if some of your customers can only accept a delivery within a certain time window?

Scenario 4: Time Windows

Sometimes your customers can only receive deliveries within certain time windows. This adds another constraint for you to think about. But it’s a simple addition to the code. Here’s how you add time windows to your input:

{
    "visits": {
        "coco_and_tengtengs": {
            "duration": 10,
            "start": "9:00",
            "end": "12:00",
            "location": {
                "lat": 49.280231,
                "lng": -123.095983
            }
        },
        "sugar_and_spice": {
            "duration": 10,
            "start": "9:00",
            "end": "12:00",
            "location": {
                "lat": 49.280232,
                "lng": -123.054692
            }
        },
        "jimmy_pestos": {
            "duration": 10,
            "start": "16:00",
            "end": "17:00",
            "location": {
                "lat": 49.268199,
                "lng": -123.003409
            }
        },
        "tiny_gyoza": {
            "duration": 15,
            "start": "13:00",
            "end": "14:00",
            "location": {
                "lat": 49.215390,
                "lng": -123.091364
            }
        },
        "finnegans": {
            "duration": 10,
            "start": "11:00",
            "end": "12:00",
            "location": {
                "lat": 49.263801,
                "lng": -123.141087
            }
        }
    },
    "fleet": {
        "vehicle_1": {
            "shift_start": "9:00",
            "shift_end": "17:00",
            "start_location": {
                "lat": 49.2847463,
                "lng": -123.1123453
            },
            "end_location": {
                "lat": 49.2847463,
                "lng": -123.1123453
            }
        }
    }
}

We can see that a single time window can be defined via start and end in the location object. Here is the response:

{
    "total_travel_time": 90,
    "total_idle_time": 182,
    "total_visit_lateness": 0,
    "total_vehicle_overtime": 0,
    "vehicle_overtime": {
        "vehicle_1": 0
    },
    "total_break_time": 0,
    "num_unserved": 0,
    "unserved": null,
    "solution": {
        "vehicle_1": [
            {
                "location_id": "vehicle_1_start",
                "location_name": "vehicle_1_start",
                "arrival_time": "11:00"
            },
            {
                "location_id": "finnegans",
                "location_name": "finnegans",
                "arrival_time": "11:09",
                "finish_time": "11:19"
            },
            {
                "location_id": "coco_and_tengtengs",
                "location_name": "coco_and_tengtengs",
                "arrival_time": "11:31",
                "finish_time": "11:41"
            },
            {
                "location_id": "sugar_and_spice",
                "location_name": "sugar_and_spice",
                "arrival_time": "11:50",
                "finish_time": "12:00"
            },
            {
                "location_id": "tiny_gyoza",
                "location_name": "tiny_gyoza",
                "arrival_time": "12:18",
                "idle_time": 42,
                "finish_time": "13:15"
            },
            {
                "location_id": "jimmy_pestos",
                "location_name": "jimmy_pestos",
                "arrival_time": "13:38",
                "idle_time": 142,
                "finish_time": "16:10"
            },
            {
                "location_id": "vehicle_1_end",
                "location_name": "vehicle_1_end",
                "arrival_time": "16:28"
            }
        ]
    },
    "total_working_time": 328,
    "status": "success",
    "num_late_visits": 0
}

We can see that the permutation and arrival times are changed compared to scenario 3 due to the time window constraint.

You’ll also notice in the output that there is a new field called idle_time — this field represents the number of minutes your drivers were not moving. This can be for a number of reasons, but the most common is that the optimal route had your drivers arrive at a location shortly before that customer was able to receive the delivery.

I've got my routes planned. Now what?

Congrats! You’ve planned out your first route with Routific’s Engine API, and you’ve learned how to account for common constraints such as:
- Shift times
- Time windows
- Durations
There’s so much more you can do with Routific’s API, and you can find all the documentation here. We’ve also put together some common recipes for you to try out on your end.
We’re excited to see what this can do for your business!