Endpoints#

Tarot Routing - Route Optimisation API

Base URL#

https://opt.route.optimiser.app

Use this URL as the base for all requests on this page.

Synchronous Route Optimisation API#

POST /v0.40/vrp#

A synchronous endpoint for Route Optimisation requests

Request Headers:
Request JSON Object:
  • jobs (list[Job]) – Jobs to be considered in the optimisation

  • drivers (list[Driver]) – Drivers available to serve those Jobs

  • settings (Settings) – Settings which govern this optimisation

Response JSON Object:
  • runs (list[Run]) – A list of optimised runs. Each Run has a “driver” and a list of “jobs

  • unserved_jobs (list[Job]) – A list of Jobs which the optimiser could not serve with the available Drivers and Constraints.

Status Codes:
Example Request
Request Body ex1.json#
{
  "drivers": [
    {
      "uid": "drvid1",
      "shift_start": 8,
      "shift_end": 17,
      "location": {"lat": -33.867798, "lon": 151.166256}
    }
  ],
  "jobs": [
    {
      "uid": "uid1",
      "duration": 2,
      "location": {"lat": -33.849489, "lon": 151.127482}
    },
    {
      "uid": "uid2",
      "duration": 2,
      "location": {"lat": -33.880661, "lon": 151.183096}
    },
    {
      "uid": "uid3",
      "duration": 2,
      "location": {"lat": -33.913168, "lon": 151.262267}
    }
  ],
  "settings": {}
}
TOKEN={your_tarot_routing_token}
curl https://opt.route.optimiser.app/v0.40/vrp \
      -H "Authorization: Token $TOKEN" \
      -H "Content-Type: application/json" \
      --data "@ex1.json"
Example Response
Response Body#
{
    "runs": [
        {
            "driver": {
                "uid": "drvid1",
                "shift_start": "08:00:00",
                "shift_end": "17:00:00",
                "run": 1,
                "seq": 0,
                "location": {
                    "lon": 151.166256,
                    "lat": -33.867798
                },
                "end_location": {
                    "lon": 151.166256,
                    "lat": -33.867798
                }
            },
            "jobs": [
                {
                    "uid": "uid1",
                    "duration": 120,
                    "eta": "08:09:45",
                    "etd": "08:11:45",
                    "run": 1,
                    "seq": 1,
                    "location": {
                        "lon": 151.127482,
                        "lat": -33.849489
                    }
                },
                {
                    "uid": "uid2",
                    "duration": 120,
                    "eta": "08:24:12",
                    "etd": "08:26:12",
                    "run": 1,
                    "seq": 2,
                    "location": {
                        "lon": 151.183096,
                        "lat": -33.880661
                    }
                },
                {
                    "uid": "uid3",
                    "duration": 120,
                    "eta": "08:42:32",
                    "etd": "08:44:32",
                    "run": 1,
                    "seq": 3,
                    "location": {
                        "lon": 151.262267,
                        "lat": -33.913168
                    }
                }
            ]
        }
    ],
    "unserved_jobs": []
}


Asynchronous Route Optimisation API#

A set of endpoints for asynchronous Route Optimisation

Learn how it works.

Start Optimisation#

POST /v0.40/polling/vrp#

Start solving your optimisation problem.

Note that the request body is identical to the Synchronous Route Optimisation API

Request Headers:
Request JSON Object:
  • jobs (list[Job]) – Jobs to be considered in the optimisation

  • drivers (list[Driver]) – Drivers available to serve those Jobs

  • settings (Settings) – Settings which govern this optimisation

Response JSON Object:
  • uid – You will use this unique identifier in subsequent requests to ask for status updates and receive the solution for this optimisation.

  • status_url – Make GET requests to this URL to understand how the optimisation is progressing, whether a solution is available, and whether the optimisation is still running.

  • solution_urlAfter a status update informs you there is a Solution, make a GET request to this URL to get the Solution. Note that this URL redirects to a pre-signed download URL for the solution, so you must follow redirects.

Status Codes:
Example Request
Request Body ex1.json#
{
  "drivers": [
    {
      "uid": "drvid1",
      "shift_start": 8,
      "shift_end": 17,
      "location": {"lat": -33.867798, "lon": 151.166256}
    }
  ],
  "jobs": [
    {
      "uid": "uid1",
      "duration": 2,
      "location": {"lat": -33.849489, "lon": 151.127482}
    },
    {
      "uid": "uid2",
      "duration": 2,
      "location": {"lat": -33.880661, "lon": 151.183096}
    },
    {
      "uid": "uid3",
      "duration": 2,
      "location": {"lat": -33.913168, "lon": 151.262267}
    }
  ],
  "settings": {}
}
TOKEN=your_tarot_routing_token
RESP=$(curl https://opt.route.optimiser.app/v0.40/polling/vrp \
      -H "Authorization: Token $TOKEN" \
      -H "Content-Type: application/json" \
      --data "@ex1.json")

VRP_UID=$(echo $RESP | jq -r .uid)
echo $VRP_UID
# 01GG7AEX026F47CS4NK2JBCE5G
Example Response
Response Body#
{
    "uid": "01GG7AEX026F47CS4NK2JBCE5G",
    "status_url": "https://opt.route.optimiser.app/v0.40/polling/vrp/01GG7AEX026F47CS4NK2JBCE5G/status",
    "solution_url": "https://opt.route.optimiser.app/v0.40/polling/vrp/01GG7AEX026F47CS4NK2JBCE5G"
}


Get Optimisation Status#

GET /v0.40/polling/vrp/{uid}/status#

Get the latest status of your optimisation request.

Parameters:
  • uid (string) – The unique identifier for this Optimisation.

Response JSON Object:
  • uid (string) – The unique identifier for this Optimisation.

  • timestamp (datetime) – The time that this status update was created in ISO8601 Format

  • message (string) – A message to let you know what the optimiser is currently doing (or why it stopped).

  • solving (bool) – true if the optimiser is still solving. false if it has finished.

  • cost (int) –

    The optimiser has a number which represents how good it’s latest solution is.

    A solution with a lower cost is better. i.e. it serves more jobs, respects more constraints, has less driving time, etc.

    The cost is dimensionless, and cannot be compared across different optimisations.

  • next_status_eta (datetime) –

    An estimate of the time that the Optimiser will provide its next status, in ISO8601 Format

    Instead of polling regularly (e.g. every 5 seconds), you should wait until the next_status_eta to GET the next status. It is only an estimate, so you should still fallback to a regular poll (e.g. every 5 seconds) in case there’s no status update by the next_status_eta.

Status Codes:

Example Request:

TOKEN=your_tarot_routing_token
VRP_UID=01GG7AEX026F47CS4NK2JBCE5G

RESP=$(curl "https://opt.route.optimiser.app/v0.40/polling/vrp/${VRP_UID}/status" \
      -H "Authorization: Token $TOKEN" \
      -H "Content-Type: application/json")

SOLVING=$(echo $RESP | jq .solving)
echo $SOLVING
# true
Example Responses
The first status after the Optimiser starts solving, before it has found a first solution. cost=0 implies no solution has been found yet.#
{
    "uid": "01GG7AEX026F47CS4NK2JBCE5G",
    "timestamp": "2022-10-25T12:19:26.129294+02:00",
    "message": "Started solving...",
    "solving": true,
    "cost": 0,
    "next_status_eta": "2022-10-25T12:19:30.129134+02:00"
}
After a first solution is found, while the optimiser is still solving#
{
    "uid": "01GG7AEX026F47CS4NK2JBCE5G",
    "timestamp": "2022-10-25T12:19:30.061413+02:00",
    "message": "Attempting to find a better solution...",
    "solving": true,
    "cost": 57516,
    "next_status_eta": "2022-10-25T12:19:34.061294+02:00"
}
solving=false indicates the optimiser has finished solving. The message lets you know why it stopped: Time Limit, Improvement Threshold, or Local Minimum if quick_opt=true.#
{
    "uid": "01GG7AEX026F47CS4NK2JBCE5G",
    "timestamp": "2022-10-25T12:19:33.421031+02:00",
    "message": "The Improvement Threshold has been reached.",
    "solving": false,
    "cost": 57516,
    "next_status_eta": null
}


Get Solution#

GET /v0.40/polling/vrp/{uid}#

This endpoint redirects to a temporary pre-signed URL which gets the (latest) Solution for your Optimisation request.

Make sure your request client follows redirects.

Note: curl does not follow redirects by default, you’ll need to use curl -L.

Parameters:
  • uid (string) – The unique identifier for this Optimisation.

Response JSON Object:
  • runs (list[Run]) – A list of optimised runs. Each Run has a “driver” and a list of “jobs

  • unserved_jobs (list[Job]) – A list of Jobs which the optimiser could not serve with the available Drivers and Constraints.

Status Codes:

Example Request:

TOKEN=your_tarot_routing_token
VRP_UID=01GG7AEX026F47CS4NK2JBCE5G

curl "https://opt.route.optimiser.app/v0.40/polling/vrp/${VRP_UID}" \
      -H "Authorization: Token $TOKEN" \
      -H "Content-Type: application/json"
Example Response
Response Body#
{
    "runs": [
        {
            "driver": {
                "uid": "drvid1",
                "shift_start": "08:00:00",
                "shift_end": "17:00:00",
                "run": 1,
                "seq": 0,
                "location": {
                    "lon": 151.166256,
                    "lat": -33.867798
                },
                "end_location": {
                    "lon": 151.166256,
                    "lat": -33.867798
                }
            },
            "jobs": [
                {
                    "uid": "uid1",
                    "duration": 120,
                    "eta": "08:09:45",
                    "etd": "08:11:45",
                    "run": 1,
                    "seq": 1,
                    "location": {
                        "lon": 151.127482,
                        "lat": -33.849489
                    }
                },
                {
                    "uid": "uid2",
                    "duration": 120,
                    "eta": "08:24:12",
                    "etd": "08:26:12",
                    "run": 1,
                    "seq": 2,
                    "location": {
                        "lon": 151.183096,
                        "lat": -33.880661
                    }
                },
                {
                    "uid": "uid3",
                    "duration": 120,
                    "eta": "08:42:32",
                    "etd": "08:44:32",
                    "run": 1,
                    "seq": 3,
                    "location": {
                        "lon": 151.262267,
                        "lat": -33.913168
                    }
                }
            ]
        }
    ],
    "unserved_jobs": []
}


End to End Polling Optimisation Example#

To make this example work, you will need

  1. A Tarot Routing account

  2. Your Tarot Routing email and password stored in environment variables TAROT_EMAIL and TAROT_PASSWORD

  3. A valid RoutingProblem file

Get your email and password into environment variables#
export TAROT_EMAIL='info@tarotanalytics.com'
export TAROT_PASSWORD='my$3cr37P@ssw0rd'
Get an Example RoutingProblem#
wget https://docs.opt.route.optimiser.app/_static/ex1.json
Get the python example code#
wget https://docs.opt.route.optimiser.app/_static/optimise_polling.py
Run the example#
pip install requests
python optimise_polling.py ex1.json
Run the example#
pip install requests
python optimise_polling.py ex1.json
A complete example of how to use the Polling Optimiser. It shows Authentication, Starting the Optimisation, Checking Status, and Getting the Solution.#
import json, os, requests, sys, time
from datetime import datetime
from zoneinfo import ZoneInfo

start_optimisation_url = 'https://opt.dev.route.optimiser.app/v0.40/polling/vrp'
auth_url = 'https://api.route.optimiser.app/api/auth/token'
headers = {'Content-Type': 'application/json'}


# Get a Token and put it in the Authorization header
body = {
    'email': os.getenv('TAROT_EMAIL'),
    'password': os.getenv('TAROT_PASSWORD'),
}
r = requests.post(auth_url, headers=headers, json=body)
token = r.json()['access_token']
headers['Authorization'] = f'Token {token}'


# Open Optimisation Problem File.
filename = sys.argv[1]
with open(filename) as f:
    body = json.load(f)


# Make the Start Optimisation request
r = requests.post(start_optimisation_url, json=body, headers=headers)

# The response gives you the URLs you need to call for the remaining requests
resp = r.json()
status_url = resp['status_url']
solution_url = resp['solution_url']


# Start the Polling Loop
solving = True
while solving:

    # Get the status, and leave the loop if the Optimiser has finished.
    r = requests.get(status_url, headers=headers)
    status = r.json()
    solving = status['solving']
    if not solving:
        # The solver has finished, exit the loop
        break

    # If you're here, the Optimiser hasn't finished yet.
    # Calculate the next time to check the status.
    next_status_eta = datetime.fromisoformat(status['next_status_eta'])
    delta = next_status_eta - datetime.now(tz=ZoneInfo('Europe/Paris'))
    time_to_next_status = delta.total_seconds()

    # Sleep until the next_status ETA,
    # but if we've already passed it 
    # we'll just try again in 5 seconds.
    time.sleep(time_to_next_status) if time_to_next_status > 0 else time.sleep(5)


# If you're here, the optimiser has finished solving.
# Let's get the Solution
r = requests.get(solution_url, headers=headers)
solution = r.json()

# Do something with it
with open(f'solution_{filename}', 'w') as f:
    json.dump(f, solution)