# GraphHopper Directions API


Integrate A-to-B route planning, turn-by-turn navigation,
route optimization, isochrone calculations, location clustering and other tools into your application.

  #### Authentication
  
  1. [Sign up for GraphHopper](https://graphhopper.com/dashboard/signup)
  2. [Create an API key](https://support.graphhopper.com/a/solutions/articles/44001976027)

  Authenticate to the API by passing your key as a query parameter in every request.

  #### API Explorer

  You can also try all API parts without registration in the [API explorer](https://explorer.graphhopper.com/).

  #### Client Libraries

  To speed up development and make coding easier, we offer a [JavaScript client](https://www.npmjs.com/package/@graphhopper/directions-api-js-client) and a
  [Java client](https://github.com/graphhopper/graphhopper/tree/master/client-hc).

  #### Plans and Rate Limits

  | Plan | Daily credits | Credits/min | Requests/sec | Max optimization vehicles | Max routing locations |
  |---|---|---|---|---|---|
  | Free (non-commercial) | 500 | limited | limited | 1 | 5 |
  | Basic | 5,000 | 100 | 1 | 2 | 30 |
  | Standard | 15,000 | 400 | 2 | 10 | 80 |
  | Premium | 50,000 | 1,000 | 10 | 20 | 200 |
  | Custom | custom | custom | custom | up to 200 | up to 10,000 |

  Rate limiting is credit-based. Every response includes `X-RateLimit-Remaining` (credits left) and `X-RateLimit-Reset` (seconds until reset). A `429` response means the limit is exhausted.

  #### Credit Costs per Request

  | Endpoint | Formula | Example |
  |---|---|---|
  | Routing | 1 credit for 2–10 locations; locations ÷ 10 above that. +1 for `alternative_route`, ×2 for round trips, ×10 for `optimize=true` | 25 locations = 2.5 credits |
  | Route Optimization | vehicles × locations (min 10, max 10 × locations). Solution polling (GET) costs 0.3 credits | 10 vehicles, 150 stops = 1,500 credits |
  | Matrix | (origins × destinations) ÷ 2, or max(origins, destinations) × 10 — whichever is cheaper (min 1) | 30 origins × 40 destinations = 400 credits |
  | Geocoding | 0.3 credits per request (default provider) | |
  | Map Matching | locations ÷ 50 (min 1) | 200 GPS points = 4 credits |
  | Isochrone | 2 credits per minute explored (min 10) | 20-min isochrone = 40 credits |
  | Cluster | customers × 10 (min 10). `as_the_crow_flies` profile: customers × 1 (min 1) | 100 customers = 1,000 credits |

  #### Common Workflows

  The APIs are designed to be combined. Here are the most common patterns:

  **Delivery / field service app** — Geocoding → Route Optimization → Routing

  1. [Geocoding](#tag/Geocoding): convert customer addresses to coordinates. Note: the Geocoding response uses `point.lng` while the Optimization request uses `lon` — map the field name accordingly.
  2. [Route Optimization](#tag/Route-Optimization) (`POST /vrp`): pass the coordinates as service addresses along with your vehicles. Set `configuration.routing.calc_points` to `true` to get route geometries in the response. The solver assigns stops to vehicles, sequences them, and returns routes with arrival times.
  3. [Routing](#tag/Routing) (`POST /route`): optionally, call this per driver route to get turn-by-turn instructions. Pass the stop coordinates from the optimization solution as `points`, set `instructions` to `true`.

  If you only need geometries (no turn-by-turn instructions), step 3 is unnecessary — `calc_points` in step 2 already provides them.

  **ETA / distance matrix** — Geocoding → Matrix

  1. [Geocoding](#tag/Geocoding): convert addresses to coordinates.
  2. [Matrix](#tag/Matrices) (`POST /matrix`): compute travel times and distances between all pairs (or specific origin/destination sets). Useful for displaying ETAs to customers or pre-filtering stops before optimization.

  **Simple A-to-B navigation** — Routing only

  Call [Routing](#tag/Routing) (`POST /route`) with two or more `points`. No optimization or geocoding needed if you already have coordinates.

  #### Optimize Response Speed

  1. Reuse SSL/TLS sessions

  You should utilize the SSL session to speed up responses after the initial response or use a library that does this. E.g. for Java the
  [OkHttp library](https://square.github.io/okhttp/) automatically reuses SSL/TLS sessions and also the browser takes care of this automatically.
  For python you can use the [`requests` library](https://requests.readthedocs.io/en/latest/user/advanced/): first you create a
  session (`session = requests.Session()`) and then do requests only with this session instead of directly using "requests".

  2. Bandwidth reduction

  If you create your own client, make sure it supports http/2 and gzipped responses for best speed.
  If you use Route Optimization or Matrices and want to solve large problems, we recommend you to reduce bandwidth
  by [compressing your POST request](https://gist.github.com/karussell/82851e303ea7b3459b2dea01f18949f4) and specifying the header
  as follows: `Content-Encoding: gzip`. This will also avoid the HTTP 413 error "Request Entity Too Large".


Version: 1.0.0

## Servers

```
https://graphhopper.com/api/1
```

## Security

### key

Type: apiKey
In: query
Name: key

## Download OpenAPI description

[GraphHopper Directions API](https://docs.graphhopper.com/_bundle/openapi.yaml)

## Route Optimization

The Route Optimization API solves [traveling salesman](https://en.wikipedia.org/wiki/Travelling_salesman_problem) and [vehicle routing problems](https://en.wikipedia.org/wiki/Vehicle_routing_problem): given a set of stops to visit and a set of vehicles to visit them with, it returns an assignment of stops to vehicles and an order to visit them in, minimizing an objective you choose (total time, number of vehicles used, longest single route, etc.) while respecting constraints you specify (time windows, vehicle capacities, driver skills, and many more).

This page describes the conceptual model. The endpoint reference pages linked at the bottom describe the request and response schema in detail. If you prefer to learn by example, jump to the [API Explorer](https://explorer.graphhopper.com/?endpoint=vrp) or the [Getting Started tutorial](https://www.graphhopper.com/blog/2019/05/17/getting-started-with-the-optimization-api-traveling-salesman-problem/).

# Mental Model

A request describes a **fleet**, a **workload**, and an **objective**. The API returns a **solution**.

The **fleet** is the set of vehicles that will execute the routes. Vehicles are described individually (start and end location, optional shift schedule); shared characteristics (routing profile, capacity, speed, cost per hour and per kilometer) are factored out into vehicle types.

The **workload** is the set of orders to be served. Each order is either a **service** (a single stop) or a **shipment** (a paired pickup and delivery that must end up on the same route, in order).

The **objective** is what the optimizer minimizes. Total time, number of vehicles used, longest single route, fairness across drivers. You choose, and you can combine multiple objectives in priority order.

A **solution** is a list of routes (one per used vehicle), each an ordered list of activities with arrival and departure times, plus an `unassigned` list for orders that could not be placed.

Two further mechanisms refine the model:

**Constraints** restrict which solutions are valid. Hard constraints (time windows, capacity, skills, allowed vehicles) leave non-conforming orders unassigned; soft constraints (priority, preferred vehicles) influence the optimizer without forbidding violations.

**Relations** link orders to each other beyond what an objective and constraints can express. They state that certain orders must be served on the same route, in a given sequence, or within a geographic flow, independently of what the optimizer would otherwise choose.

These five elements — fleet, workload, objective, constraints, relations — are everything you ever describe in a request. The rest of this page explains how to use each of them well.

# Minimal Example

A request with one vehicle, one vehicle type, four services, a relation, and an objective to minimize total time. One service exceeds the vehicle's capacity and will be left unassigned. The `in_direct_sequence` relation forces order-3 to be served immediately after the vehicle's start, followed immediately by order-2:

```json
{
  "vehicles": [
    {
      "vehicle_id": "driver-1",
      "type_id": "van",
      "start_address": { "location_id": "depot", "lon": 13.3888, "lat": 52.5170 },
      "earliest_start": 1746612000
    }
  ],
  "vehicle_types": [
    {
      "type_id": "van",
      "profile": "car",
      "capacity": [10]
    }
  ],
  "services": [
    {
      "id": "order-1",
      "address": { "location_id": "order-1", "lon": 13.3762, "lat": 52.5206 },
      "size": [2],
      "duration": 300,
      "time_windows": [{ "earliest": 1746612000, "latest": 1746615600 }]
    },
    {
      "id": "order-2",
      "address": { "location_id": "order-2", "lon": 13.4050, "lat": 52.5200 },
      "size": [3],
      "duration": 300
    },
    {
      "id": "order-3",
      "address": { "location_id": "order-3", "lon": 13.4270, "lat": 52.5230 },
      "size": [1],
      "duration": 300
    },
    {
      "id": "order-4",
      "address": { "location_id": "order-4", "lon": 13.395163, "lat": 52.526215 },
      "size": [20],
      "duration": 300
    }
  ],
  "objectives": [
    { "type": "min", "value": "completion_time" }
  ],
  "relations": [ { "type": "in_direct_sequence", "ids": ["start","order-3","order-2"] } ],
  "configuration": { "routing": { "calc_points": true } }
}
```

The response contains a `solution` with a `routes` array (one per used vehicle) and an `unassigned` object for orders that could not be placed:

```json
{
  "solution": {
    "distance": 10424,
    "transport_time": 1279,
    "completion_time": 2179,
    "no_vehicles": 1,
    "no_unassigned": 1,
    "routes": [
      {
        "vehicle_id": "driver-1",
        "points": [
          { "type": "LineString", "coordinates": [[13.3888,52.517],"...",  [13.427,52.523]] },
          { "type": "LineString", "coordinates": [[13.427,52.523], "...", [13.405,52.52]] },
          { "type": "LineString", "coordinates": [[13.405,52.52],  "...", [13.3762,52.5207]] },
          { "type": "LineString", "coordinates": [[13.3762,52.5207],"...",[13.3888,52.517]] }
        ],
        "activities": [
          { "type": "start", "location_id": "depot", "arr_time": 1746612000, "end_time": 1746612000, "load_after": [0] },
          { "type": "service", "id": "order-3", "location_id": "order-3", "arr_time": 1746612352, "end_time": 1746612652, "load_before": [0], "load_after": [1] },
          { "type": "service", "id": "order-2", "location_id": "order-2", "arr_time": 1746612966, "end_time": 1746613266, "load_before": [1], "load_after": [4] },
          { "type": "service", "id": "order-1", "location_id": "order-1", "arr_time": 1746613640, "end_time": 1746613940, "load_before": [4], "load_after": [6], "time_window": { "earliest": 1746612000, "latest": 1746615600 } },
          { "type": "end", "location_id": "depot", "arr_time": 1746614179, "end_time": 1746614179, "load_before": [6] }
        ]
      }
    ],
    "unassigned": {
      "services": ["order-4"],
      "details": [{ "id": "order-4", "code": 3, "reason": "does not fit into any vehicle due to capacity" }]
    }
  }
}
```

Note that `time_windows` use Unix timestamps (recommended). The `in_direct_sequence` relation forced the solver to visit order-3 then order-2 immediately after the start, even though a different sequence might have been shorter. `order-4` was left unassigned with reason code 3 (capacity exceeded: size 20 vs. vehicle capacity 10). Because `configuration.routing.calc_points` is `true`, the response includes a `points` array with one LineString geometry per leg — use these to render routes on a map without a separate Routing API call.

# Concepts

## Services vs. Shipments

The choice matters because it changes what the optimizer is allowed to do.

A **service** is loaded onto the vehicle for the rest of the route (if `type` is `service` or `pickup`) or treated as already-loaded at the depot and dropped at the customer (`type: delivery`). The optimizer is free to interleave services from different customers in any order that satisfies constraints.

A **shipment** introduces an ordering constraint: the pickup of shipment X must happen before the delivery of shipment X, on the same route. Between those two stops, the optimizer can interleave other services or other shipments' pickups and deliveries, as long as capacity holds.

Use services for typical last-mile delivery (where goods come from a single depot), customer visits, technician dispatch. Use shipments when goods move directly between customer locations: courier work, ride-hailing, moving services, vehicle repositioning.

## Constraints: Hard vs. Soft

Constraints fall into two categories, and understanding the difference is critical for diagnosing why the solver returned the result it did.

**Hard constraints** must be satisfied. If they cannot be, the order is left unassigned and appears in `solution.unassigned`. Hard constraints include:

- Time windows on services, shipments, and vehicle shifts
- Vehicle capacity vs. order size
- Skills (a service's `required_skills` must be a subset of the assigned vehicle's `skills`)
- `allowed_vehicles` and `disallowed_vehicles` lists
- `max_distance`, `max_driving_time`, `max_jobs`, `max_activities` per vehicle
- `max_time_in_vehicle` for deliveries
- Relation constraints (`in_sequence`, `in_direct_sequence`, `in_same_route`, `in_area_sequence`)

**Soft constraints** influence the solution but do not prevent assignment. Violating them costs the optimizer "score," so it tries to avoid them, but will accept a violation if there's no alternative. Soft constraints include:

- `priority` on services and shipments (high-priority orders get assigned first when not all can be served)
- `preferred_vehicles`
- `min_jobs` per vehicle

When an order ends up unassigned, the response includes `solution.unassigned.details` with a code indicating the most likely reason (capacity, time window, skill mismatch, etc.) and a `reasons` array with all probable causes ranked by likelihood. The full code table is in the [POST /vrp reference](#operation/solveVRP).

## Relations: Shaping the Solution

The objectives plus hard and soft constraints can express most of what a router needs, but not everything. Real-world dispatchers carry knowledge that resists formalization: a driver prefers to clear the north side of town before lunch, two stops at the same office should be done back-to-back so the driver only signs in once, a particular customer expects to be the first visit of the day, the morning route should sweep west-to-east before circling back. These aren't gaps in the API — they're the gap between any abstract objective and the messy reality of operations. Drivers have preferences, customers have habits, and dispatchers have judgment that earned its keep over years.

**Relations are the escape hatch for that gap.** Instead of trying to encode every preference as an objective or constraint, you let the optimizer do its job within boundaries you define. A relation says "whatever else you decide, this constraint must hold."

The available relation types:

- **`in_same_route`** — the related items must end up on the same vehicle, regardless of order.
- **`not_in_same_route`** — the related items must end up on different vehicles. This is the only exclusion relation; all others pull items together.
- **`in_sequence`** — the related items must appear in the listed order on a single route, but other stops may be inserted between them.
- **`in_direct_sequence`** — the related items must appear in the listed order on a single route, with no stops in between.
- **`neighbor`** — the related items must be served by the same vehicle and appear next to each other, but in any order.
- **`in_area_sequence`** — each area is defined by an anchor stop and a radius; all stops within that radius belong to the area. The areas must be visited in the given order, while the optimizer remains free to sequence stops within each area.

`in_area_sequence` is unique among the six in that the grouping is defined spatially, by an anchor stop and a radius, rather than by labels you assign to individual orders. A dispatcher picks a few anchor stops, sets a radius for each, orders the resulting areas, and the optimizer respects the geographic flow while still finding the best sequence within each area. The dispatcher's tacit knowledge about traffic, neighborhood characteristics, or driver preference becomes a few clicks; the optimizer handles the rest.

### Two Granularities: Orders and Groups

Relations operate at two granularities, depending on which field you populate.

**Order-level relations** use the `ids` field, listing individual service or shipment ids. `in_sequence` over `["service-A", "service-B"]` means service-A must be served before service-B on the same route.

**Group-level relations** use the `group` field, listing group labels instead of order ids. `in_sequence` over groups `["downtown", "uptown"]` is a higher-order constraint: every stop tagged `group: "downtown"` must be served before every stop tagged `group: "uptown"`. The relation operates on the clusters as units, not on the individual stops inside them.

Group-level relations are what make interactive UIs practical. A dispatcher assigns a group label to a set of stops in a neighborhood, and a single relation entry sequences that group against another, regardless of how many stops are inside either one. The dispatcher works with a handful of meaningful clusters; the request stays compact regardless of how many orders the underlying problem contains.

### Groups Own Semantics

Groups are not only labels you reference from relations. Assigning the same group label to two or more orders is itself a constraint.

Tagging orders with the same group does two things at once. First, it treats them as a contiguous cluster: wherever they are served, they appear as a block, with no other stops inserted between them. Second, it leaves the order within that block to the optimizer. A group is in effect a "weak in_sequence": the stops stay together, but the algorithm picks the best internal sequence.

The cluster constraint is about contiguity, not route assignment. In a multi-vehicle problem, the orders in group A and the orders in group B can land on different routes; the optimizer is free to make that choice. On each route a group appears on, its members are contiguous. If you want all members of a group on the same route, add an `in_same_route` relation over the group, or pin the group to a specific vehicle by adding a `vehicle_id` to the relation.

Ungrouped orders are by default not allowed to be inserted inside a group; if you need that flexibility, set `configuration.optimization.free_insertion` to true. You can then exempt specific groups (typically conventional ones like `"asap"` or `"last"`) from free insertion via `free_insertion_exempt_groups`, so those groups remain compact while other ungrouped orders move freely.

### Practical Notes

Relations are hard constraints: an order in an unsatisfiable relation appears in `unassigned` with reason code 21 (or 29 for group constraints). They compose with all other constraints, so a relation that conflicts with a time window or capacity will leave the affected orders unassigned.

Relations can also be built dynamically. A common pattern is to take the result of a first optimization, extract the sequence the optimizer chose, and feed it back as `in_direct_sequence` relations to lock that sequence in for the next request. This is useful when re-planning around late-breaking changes without disrupting an already-communicated route. Watch for cases where the new constraints over-constrain the problem and previously-assignable orders become unassigned.

## Choosing an Objective

Pick the objective that matches what you're actually optimizing for. The defaults that suit most use cases:

**Just minimize total time:** `min completion_time`. This is the right answer for ~80% of real dispatch problems.

**Use as few vehicles as possible, then minimize time:** combine `min vehicles` followed by `min completion_time`. The order matters: the optimizer treats objectives lexicographically, so vehicles are minimized first, completion time second.

**Finish all routes as early as possible (minimize the longest route):** `min-max completion_time`. Useful when you have a hard end-of-day deadline and want all drivers home around the same time.

**Distribute work fairly across drivers:** `balance completion_time` (Beta) or `balance activities`. The `balance` type minimizes the variance of workload, while `min-max` only minimizes the longest route. Use `balance` when fairness matters; use `min-max` when only the latest finish time matters.

**Travel time only, ignoring waiting at customer sites:** `min transport_time`. Rare. Most users actually want `completion_time`.

For a deeper discussion, see [completion time vs. transport time](https://www.graphhopper.com/blog/2016/06/20/what-is-the-difference-between-the-minimization-of-completion-time-and-minimizing-transport-time/).

## Synchronous vs. Asynchronous

Two endpoints solve the same problem with different delivery mechanisms.

**`POST /vrp`** (synchronous) returns the solution in the response body. Use this when the problem is small enough to solve in under 10 seconds. If the solver runs longer, the request times out and you lose the work.

**`POST /vrp/optimize`** (asynchronous) returns a `job_id` immediately. You then poll **`GET /vrp/solution/{jobId}`** every 500ms or so until `status` is `finished`. Use this for any problem that might exceed 10 seconds.

As a rule of thumb, prefer the synchronous endpoint for problems with a single vehicle and up to 200 stops. If you're unsure, start synchronous; if you hit timeouts, switch to async. The async workflow has the same request body, so switching is a one-line change.

## Endpoints

- [POST /vrp](#operation/solveVRP): synchronous endpoint. Returns the solution in the response. Suitable for problems that solve in under 10 seconds.
- [POST /vrp/optimize](#operation/asyncVRP): asynchronous endpoint. Returns a `job_id`; fetch the solution from the endpoint below. Use this for problems that take longer than 10 seconds. The request body is identical to `/vrp`.
- [GET /vrp/solution/{jobId}](#operation/getSolution): poll this endpoint until `status` is `finished`. Recommended interval: 500ms.

## Further reading

- [Getting Started with the Optimization API](https://www.graphhopper.com/blog/2019/05/17/getting-started-with-the-optimization-api-traveling-salesman-problem/) — step-by-step walkthrough of a first VRP request
- [Area Sequencing: shape your routes by area](https://www.graphhopper.com/blog/2026/05/04/area-sequencing-shape-your-routes-by-area/) — using `in_area_sequence` to control geographic flow
- [Solving a TSP with a week-planning horizon and driver shifts](https://www.graphhopper.com/blog/2020/07/15/how-to-solve-a-traveling-salesman-problem-with-a-week-planning-horizon-and-driver-shifts/)
- [Scheduling technicians with skills and multiple dependencies](https://www.graphhopper.com/blog/2016/06/03/how-to-route-technicians-with-skills-and-multiple-dependencies-between-tasks/)
- [Completion time vs. transport time](https://www.graphhopper.com/blog/2016/06/20/what-is-the-difference-between-the-minimization-of-completion-time-and-minimizing-transport-time/)
- [Modeling multi-trip routes for a single vehicle](https://www.graphhopper.com/blog/2016/07/21/how-to-model-multiple-delivery-routes-with-a-single-vehicle/)


### Solve a route optimization problem

 - [POST /vrp](https://docs.graphhopper.com/openapi/route-optimization/solvevrp.md): Start by reading the introduction to the Route Optimization API.

To solve a new vehicle routing problem, make a HTTP POST to this URL


https://graphhopper.com/api/1/vrp?key=


It returns the solution to this problem in the JSON response.

Please note that this endpoint is well suited for solving smaller problems.
Larger vehicle routing problems that take longer than 10 seconds to solve cannot be processed using this endpoint.
To solve them, please use the batch mode URL instead.

### Submit a route optimization job

 - [POST /vrp/optimize](https://docs.graphhopper.com/openapi/route-optimization/asyncvrp.md): To solve a vehicle routing problem, perform the following steps:

1.) Make a HTTP POST to this URL


https://graphhopper.com/api/1/vrp/optimize?key=


It returns a job id (job_id).

2.) Take the job id and fetch the solution for the vehicle routing problem from this URL:


https://graphhopper.com/api/1/vrp/solution/?key=


We recommend querying the solution every 500ms until it returns 'status=finished'.

Note: Since the workflow is more complex and you lose some time fetching the solution, you should prefer
the synchronous endpoint when possible. Use the batch mode only for long-running problems.

### Retrieve solution of a route optimization job

 - [GET /vrp/solution/{jobId}](https://docs.graphhopper.com/openapi/route-optimization/getsolution.md): Take the job id and fetch the solution for the vehicle routing problem from this URL:


https://graphhopper.com/api/1/vrp/solution/?key=


You get the job id by sending a vehicle routing problem to the batch mode URL.

## Routing

The Routing API calculates the best path connecting two or more points, where the meaning of ''best'' depends on the vehicle profile and use case.
Besides path coordinates it can return turn-by-turn instructions, elevation, [path details](https://www.graphhopper.com/blog/2019/11/28/routing-api-using-path-details/) and other useful information about the route.

Use our [API Explorer](https://explorer.graphhopper.com/) to explore the Routing API.


### Calculate a route

 - [POST /route](https://docs.graphhopper.com/openapi/routing/postroute.md): This POST endpoint accepts routing requests as JSON and returns the computed route as JSON. It offers a high degree of customization through the custom model to account for 
vehicle constraints such as height or weight, avoidance or exclusion of specific areas. It also provids rich route information including elevation data, turn-by-turn instructions,
toll information, and other detailed path details.

### Calculate a route

 - [GET /route](https://docs.graphhopper.com/openapi/routing/getroute.md): For the GET request you specify the parameters in the URL and can try it directly in every browser.
However, it has some disadvantages when using many points (URL length limit) and the custom_model Feature cannot be used. 
Therefore, our recommended endpoint is the POST route endpoint.

## Matrices

The Matrix API calculates distances and times between multiple start and destination locations.
It can compute full NxN matrices or asymmetric matrices with separate origin and destination sets.


### Compute a matrix

 - [POST /matrix](https://docs.graphhopper.com/openapi/matrices/postmatrix.md): Calculate a matrix of travel times and/or distances between N origins and M destinations.

This includes the common cases of routes from one origin to many destinations, or from many origins to one
destination.

### Compute a matrix

 - [GET /matrix](https://docs.graphhopper.com/openapi/matrices/getmatrix.md): For N origins and M destinations, compute routes from all origins to all destinations and output
the result as a matrix of travel times and/or distances.

This includes the common cases of routes from one origin to many destinations, or from many origins to one
destination.

### Submit a matrix computation job

 - [POST /matrix/calculate](https://docs.graphhopper.com/openapi/matrices/calculatematrix.md): An alternate endpoint for computing a large matrix asynchronously, where a request against the regular 
endpoint would result in a timeout.

The request format is the same, but instead of the result, you are given a job identification number that
you can use to retrieve the result once it is available.
        
In most cases, prefer the regular endpoints.

Here are some full examples via curl:
bash
$ curl -X POST -H "Content-Type: application/json" "https://graphhopper.com/api/1/matrix/calculate?key=[YOUR_KEY]" -d '{"points":[[13.29895,52.48696],[13.370876,52.489575],[13.439026,52.511206]]}'
{"job_id":"7ac65787-fb99-4e02-a832-2c3010c70097"}


Pick the returned job_id and use it in the next GET requests:
bash
$ curl -X GET "https://graphhopper.com/api/1/matrix/solution/7ac65787-fb99-4e02-a832-2c3010c70097?key=[YOUR_KEY]"
{"status":"waiting"}


When the calculation is finished (status:finished) the JSON response will contain the full matrix JSON under solution:
bash
$ curl -X GET "https://graphhopper.com/api/1/matrix/solution/7ac65787-fb99-4e02-a832-2c3010c70097?key=[YOUR_KEY]"
{"solution":{"weights":[[0.0,470.453,945.414],[503.793,0.0,580.871],[970.49,569.511,0.0]],"info":{"copyrights":["GraphHopper","OpenStreetMap contributors"]}},"status":"finished"}


Please note that if an error occured while calculation the JSON will not have a status but contain directly the error message e.g.:
json
{"message":"Cannot find from_points: 1"}

And the optional hints array.

### Retrieve result of a matrix computation job

 - [GET /matrix/solution/{jobId}](https://docs.graphhopper.com/openapi/matrices/getmatrixsolution.md)

## Geocoding

_Geocoding_ describes the process of transforming an textual address representation to a coordinate (`latitude,longitude`).
For example the conversion from `Berlin` to `52.5170365,13.3888599`.

_Reverse geocoding_ converts a coordinate to a textual address representation or place name. Find out more about Geocoding itself on [Wikipedia](http://en.wikipedia.org/wiki/Geocoding).


### Geocoding Endpoint

 - [GET /geocode](https://docs.graphhopper.com/openapi/geocoding/getgeocode.md)

## Isochrones

An isochrone of a location is ''a line connecting points at which a vehicle arrives at the same time'', see Wikipedia.
With the same API you can also calculate isodistances, just use the parameter distance_limit instead of time_limit`.

Some possible areas in which this API may be useful to you:

- real estate analysis
- realtors
- vehicle scheduling
- geomarketing
- reach of electric vehicles
- transport planning
- logistics (distribution and retail network planning)

See the [clients](#section/API-Clients) section in the main documentation, and [our API explorer](https://explorer.graphhopper.com/?endpoint=isochrone).


### Compute an isochrone

 - [GET /isochrone](https://docs.graphhopper.com/openapi/isochrones/getisochrone.md)

## Map Matching

You can snap measured GPS points typically as GPX files to a digital
road network to e.g. clean data or attach certain data like elevation or turn instructions to it.

See the [clients](#section/API-Clients) section in the main documentation, and [our API explorer](https://explorer.graphhopper.com/?endpoint=match).

The cost for one request depends on the number of GPS location and is documented [here](https://support.graphhopper.com/support/solutions/articles/44000718211-what-is-one-credit-).

One request should not exceed the Map Matching API location limit depending on the package, see the pricing in our dashboard.


### Map-match a GPX file

 - [POST /match](https://docs.graphhopper.com/openapi/map-matching/postgpx.md): To get a match response you send a GPX file in the body of an HTTP POST request and specify request parameters like the key and profile in the URL.
See below for more supported parameters.

## Clustering

It solves the “capacity clustering problem” by assigning a set of customers to a given number of distinct groups (called clusters).
The API “clusters” by minimizing the total distance from each individual customer to its designated group median.
It can also consider minimum and maximum capacity restrictions for each group.

Clustering can be used in many practical applications.
For example, it can help to plan territories, i.e. territory optimization for field teams with large territories for field workers,
or to solve large vehicle routing problems (VRP).

Try Clustering in our [API Explorer](https://explorer.graphhopper.com/?endpoint=cluster)!

The idea is to divide a certain number of customers, a pre-specified number of clusters. As already written above, a distribution is sought that minimizes the total cost (e.g. distance or time or a function of distance and time).
We currently support two approaches.

1. You can simply define a certain number of clusters via configuration ("clustering" with empty set of "clusters") and additionally how many customers should be in such a cluster.
This is defined by an upper and lower limit ("min_quantity" and "max_quantity). The algorithm then searches for suitable clusters and divides the customers into these clusters.

2. You can explicitly define clusters via "clusters". In this way, each individual cluster can be defined.
This approach not only allows each cluster to have its own capacity upper and lower bound, but each cluster can also be assigned a fixed cluster center.
In contrast to 1. the algorithm then does not search for a suitable center, but assigns the customers given the fixed centers to each cluster. Note that if you define clusters
explicitly, any configuration of "clustering" will be overwritten by these explicit clusters.


### Solve a clustering problem

 - [POST /cluster](https://docs.graphhopper.com/openapi/clustering/solveclusteringproblem.md): The Cluster endpoint is used with a POST request towards
https://graphhopper.com/api/1/cluster?key=. The solution will be provided in the JSON response.
Please note that for problems that take longer than 10 seconds a bad request error is returned.
In this case please use the asynchronous Batch Cluster Endpoint instead.

### Submit a clustering job

 - [POST /cluster/calculate](https://docs.graphhopper.com/openapi/clustering/asyncclusteringproblem.md): Prefer the synchronous endpoint and use this Batch Cluster endpoint for
long running problems only. The work flow is asynchronous:

- send a POST request towards https://graphhopper.com/api/1/cluster/calculate?key= and fetch the job_id.
- poll the solution every 500ms until it gives status=finished. Do this with a GET request
  towards https://graphhopper.com/api/1/cluster/solution/?key=.

### Retrieve solution of a clustering job

 - [GET /cluster/solution/{jobId}](https://docs.graphhopper.com/openapi/clustering/getclustersolution.md): This endpoint returns the solution of the clustering problems submitted to the Batch Cluster endpoint.
You can fetch it with the job_id, you have been sent.

## Custom Profiles


You can create routing profiles that are customized to your needs. You can take advantage of
all the modelling options described in the [Custom Model section](#tag/Custom-Model) and use the created custom profile (prefix `cp_`)
with our Routing, Matrix and Route Optimization APIs.

**Notes**

 * The geographic coverage of each custom profile is restricted and varies based on the selected package.
 * This feature is available starting with the premium package.

A curl example to create a profile:

```
curl -X POST -H "Content-Type: application/json" "https://graphhopper.com/api/1/profiles?key=YOUR_KEY" -d '{"bounds":{"bbox":[11.45462,48.00954,11.77322,48.2076]},"custom_model":{"priority":[{"if":"road_class == MOTORWAY","multiply_by":"0"}]},"profile":"car"}'
```

To quickly test your custom_model you can use the Routing API where a different custom model can be specified in every request.
Or use [GraphHopper Maps](https://graphhopper.com/maps/) and click the gear button.

**Creating custom profiles using the API Explorer**

Besides using the `/profiles` endpoint directly you can also create custom profiles using [our API explorer](https://explorer.graphhopper.com/?endpoint=profiles).

 1. Visit the [API explorer](https://explorer.graphhopper.com/?endpoint=profiles).
 2. Set your API key with the "API key" button.
 2. Now copy and paste the JSON to create a custom profile into the input window. To get started you can use the already pre-filled example, which will create a
    custom profile that excludes motorways and is limited to the Munich area.
 3. Click "Send". This creates a custom profile. Copy the returned `id` from the output window (it starts with `cp_`).
 4. To try this profile out you change the drop down to "Optimization API", pick the first example and replace `car` in `"profile": "car"` (`vehicle_types` section) with the profile `id` you just copied and click "Send".

The optimized route no longer uses motorways. Keep in mind that this is a simple example and the custom model language
is a lot more powerful than this with which you can avoid, exclude or prefer certain areas or road classes and a lot more.
Make sure you read the [Custom Model section](#tag/Custom-Model) to learn about all the details.

Note that you can use the profile `id` just as well for the `/matrix` or `/route` endpoint. E.g. select "Routing API"
and use the profile `id` in the request.


### Create a custom routing profile

 - [POST /profiles](https://docs.graphhopper.com/openapi/custom-profiles/postprofile.md)

### List your custom routing profiles

 - [GET /profiles](https://docs.graphhopper.com/openapi/custom-profiles/getprofile.md)

### Submit a profile creation job

 - [POST /profiles/calculate](https://docs.graphhopper.com/openapi/custom-profiles/calculateprofile.md): An alternate endpoint for computing a profile for a large boundary asynchronously, where a request against the regular
endpoint would result in a timeout.

The request format is the same, but instead of the result, you are given a job identification number that
you can use to retrieve the result once it is available.

### Retrieve result of a profile creation job

 - [GET /profiles/solution/{jobId}](https://docs.graphhopper.com/openapi/custom-profiles/getprofilesolution.md)

### Delete a custom routing profile

 - [DELETE /profiles/{profileId}](https://docs.graphhopper.com/openapi/custom-profiles/deleteprofile.md)

