Plan re-costing
Price an edited plan exactly as given — per-leg distance, duration and road geometry, with arrival times — without resequencing it. Stated constraints (time windows, capacity, skills, vehicle ceilings) are judged and reported as violations, never refused.
plan.recost
Stable
node
How to call it
REST
POST
/v1/plan/recost
GraphQL
query
openPlanRecost
MCP
open_plan_recost
Price a plan exactly as given, without resequencing it: every leg measured on the road network with distance, duration and geometry, and arrival times projected from departure. Use it after editing a plan — adding a stop at a chosen position, removing one, reordering or reversing a route, or moving a start or end point. To have the engine PLACE a new stop instead, use open_routing_refine with the existing stops frozen. Billed per stop.
Request schema
Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.
POST /v1/plan/recost
{
"type": "object",
"properties": {
"departed_at": [],
"jobs": {
"type": "array",
"minItems": 1,
"maxItems": 2000,
"items": {
"type": "object",
"properties": {
"amount": {
"type": [
"array",
"null"
]
},
"id": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"location": {
"type": "array"
},
"service_seconds": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"skills": {
"type": "array",
"items": {
"type": "string",
"maxLength": 64
}
},
"time_windows": {
"type": [
"array",
"null"
]
}
},
"required": [
"location"
]
}
},
"matrix": {
"type": [
"string",
"null"
],
"maxLength": 200
},
"options": {
"type": [
"object",
"null"
],
"properties": {
"geometry": {
"type": [
"boolean",
"null"
]
}
}
},
"profile": {
"type": [
"string",
"null"
],
"enum": [
"car",
"van",
"truck",
"bike",
"foot",
"motorcycle"
]
},
"region": {
"type": [
"string",
"null"
],
"enum": [
"NA",
"EU",
"AU",
"ASIA",
"na",
"eu",
"au",
"asia"
]
},
"routes": {
"type": "array",
"minItems": 1,
"maxItems": 200,
"items": {
"type": "object",
"properties": {
"reverse": {
"type": [
"boolean",
"null"
]
},
"stops": {
"type": "array",
"maxItems": 2000,
"items": {
"type": "string",
"maxLength": 128
}
},
"vehicle_id": {
"type": "string",
"maxLength": 128
}
},
"required": [
"vehicle_id"
]
}
},
"vehicles": {
"type": "array",
"minItems": 1,
"maxItems": 200,
"items": {
"type": "object",
"properties": {
"capacity": {
"type": [
"array",
"null"
]
},
"end": {
"type": [
"array",
"null"
]
},
"id": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"max_distance_meters": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"max_tasks": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"max_travel_time_seconds": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"return_to_start": {
"type": [
"boolean",
"null"
]
},
"skills": {
"type": "array",
"items": {
"type": "string",
"maxLength": 64
}
},
"start": {
"type": [
"array",
"null"
]
},
"time_window": {
"type": [
"array",
"null"
]
}
}
}
}
},
"additionalProperties": false,
"required": [
"jobs",
"routes",
"vehicles"
]
}
Request example
{
"vehicles": [
{
"id": "van-1",
"start": {
"lat": 45.5017,
"lng": -73.5673
},
"return_to_start": true,
"capacity": [
10
]
}
],
"jobs": [
{
"id": "order-1001",
"location": {
"lat": 45.5088,
"lng": -73.554
},
"service_seconds": 300,
"amount": [
2
]
},
{
"id": "order-1002",
"location": {
"lat": 45.4972,
"lng": -73.5786
},
"service_seconds": 300,
"amount": [
3
]
}
],
"routes": [
{
"vehicle_id": "van-1",
"stops": [
"order-1001",
"order-1002"
]
}
]
}
Response
What a successful call returns: the schema of `data` and `meta`, with a description of each field, followed by an example response from a real call.
POST /v1/plan/recost
Response schema
{
"description": "The caller's routes, kept exactly as given, with distance, driving time and arrival time of every stop. Breaches of the stated constraints are reported in `violations`, never refused.",
"data": {
"type": "object",
"properties": {
"routes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"vehicle_id": {
"type": "string"
},
"stop_count": {
"type": "integer",
"description": "Stops including start and end."
},
"task_count": {
"type": "integer",
"description": "Jobs on the route."
},
"distance_meters": {
"type": "integer"
},
"duration_seconds": {
"type": "integer",
"description": "Driving time."
},
"service_seconds": {
"type": "integer"
},
"waiting_seconds": {
"type": "integer"
},
"departure": {
"type": "string",
"description": "ISO 8601 with offset."
},
"completion": {
"type": "string",
"description": "When the last stop (or the end) is reached. ISO 8601 with offset."
},
"unreachable_legs": {
"type": "integer"
},
"violations": {
"type": "array",
"items": {
"type": "object",
"description": "A route-level breach. Which fields are present depends on `rule`.",
"properties": {
"rule": {
"type": "string",
"enum": [
"capacity",
"max_tasks",
"max_distance",
"max_travel_time",
"vehicle_time_window"
]
},
"message": {
"type": "string"
},
"dimension": {
"type": "string",
"description": "capacity: the capacity dimension that overflows."
},
"load": {
"type": "number",
"description": "capacity: what the stops load."
},
"capacity": {
"type": "number",
"description": "capacity: what the vehicle carries."
},
"tasks": {
"type": "integer",
"description": "max_tasks: stops on the route."
},
"distance_meters": {
"type": "integer",
"description": "max_distance: distance of the route."
},
"duration_seconds": {
"type": "integer",
"description": "max_travel_time: driving time of the route."
},
"limit": {
"type": "integer",
"description": "max_tasks, max_distance, max_travel_time: the vehicle's limit."
},
"late_seconds": {
"type": "integer",
"description": "vehicle_time_window: how far past the working window the route ends."
},
"window_end": {
"type": "string",
"description": "vehicle_time_window: when the working window ends. ISO 8601 with offset."
}
}
},
"description": "Route-level breaches."
},
"violation_count": {
"type": "integer",
"description": "Route and stop breaches on this route."
},
"stops": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"start",
"job",
"end"
]
},
"id": {
"type": "string",
"description": "The caller's job id. Jobs only."
},
"sequence": {
"type": "integer",
"description": "Position of the job on the route, from 1."
},
"reachable": {
"type": "boolean",
"description": "Whether the hop into this stop could be routed. An unreachable hop has no distance or time and is counted in `unreachable_legs`."
},
"departure": {
"type": "string",
"description": "Start only: when the vehicle leaves. ISO 8601 with offset."
},
"waiting_seconds": {
"type": "integer",
"description": "Start only, when the vehicle must wait for its working window to open."
},
"arrival": {
"type": [
"string",
"null"
],
"description": "ISO 8601 with offset. Null after an unreachable hop."
},
"arrival_epoch": {
"type": [
"integer",
"null"
],
"description": "`arrival` as Unix seconds."
},
"leg_distance_meters": {
"type": [
"integer",
"null"
],
"description": "Distance of the hop into this stop."
},
"leg_duration_seconds": {
"type": [
"integer",
"null"
],
"description": "Driving time of the hop into this stop."
},
"leg_geometry": {
"type": [
"string",
"null"
],
"description": "Encoded polyline (precision 5) of the hop into this stop. Present when `options.geometry` is true (the default)."
},
"distance_meters": {
"type": [
"integer",
"null"
],
"description": "Cumulative distance from the start."
},
"duration_seconds": {
"type": [
"integer",
"null"
],
"description": "Cumulative driving time from the start."
},
"violations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"rule": {
"type": "string",
"enum": [
"skills",
"time_window"
]
},
"message": {
"type": "string"
},
"missing": {
"type": "array",
"items": {
"type": "string"
},
"description": "skills: the skills the vehicle lacks."
},
"late_seconds": {
"type": "integer",
"description": "time_window: how late the arrival is."
},
"window": {
"type": "object",
"description": "time_window: the window that was missed.",
"properties": {
"from": {
"type": "string",
"description": "ISO 8601 with offset."
},
"to": {
"type": "string",
"description": "ISO 8601 with offset."
}
}
}
}
},
"description": "Constraints this stop breaks. Present only when it breaks one."
}
},
"required": [
"type"
]
}
}
}
}
},
"summary": {
"type": "object",
"properties": {
"routes": {
"type": "integer"
},
"tasks": {
"type": "integer",
"description": "Jobs across all routes."
},
"distance_meters": {
"type": "integer"
},
"duration_seconds": {
"type": "integer",
"description": "Driving time across all routes."
},
"unreachable_legs": {
"type": "integer",
"description": "Hops that could not be routed."
},
"violations": {
"type": "integer",
"description": "Breaches across all routes and stops."
}
}
}
}
},
"meta": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The engine region used."
},
"profile": {
"type": "string",
"description": "The vehicle profile used."
},
"geometry": {
"type": "boolean",
"description": "Whether leg geometry was returned."
},
"geometry_format": {
"type": [
"string",
"null"
],
"description": "`polyline` when geometry was returned."
},
"departed_at": {
"type": "string",
"description": "The departure time the plan was timed from: `departed_at`, else the current time. ISO 8601 with offset."
}
},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "plan.recost",
"billing": {
"unit": "node",
"quantity": 2,
"counted": true,
"breakdown": {
"tasks": 2,
"routes": 1
}
},
"data": {
"routes": [
{
"vehicle_id": "van-1",
"stop_count": 4,
"task_count": 2,
"distance_meters": 6416,
"duration_seconds": 832,
"service_seconds": 600,
"waiting_seconds": 0,
"departure": "2026-10-23T09:27:48-04:00",
"completion": "2026-10-23T09:51:40-04:00",
"unreachable_legs": 0,
"violations": [],
"violation_count": 0,
"stops": [
{
"type": "start",
"departure": "2026-10-23T09:27:48-04:00"
},
{
"type": "job",
"id": "order-1001",
"sequence": 1,
"reachable": true,
"leg_distance_meters": 1840,
"leg_duration_seconds": 245,
"leg_geometry": "y`vtGjr_`MJUNYL[h@gAHOFQBIHOc@g@MOa@]s@q@EGEEk@g@k@k@CCMMDO@EDMBMBOBQBO@KBM?G@OBQ?G@MBQ@MBMBMBOBOBMFSDSBGBIBGHOvAkE@EHQGEECa@_@ECMKKIwAiA_CmBq@m@ECIIOKoAcAs@i@KIIIOMgA{@QMCCGGGE_@[m@g@a@[kA_ACCECKGKGWSQOwAkAGEGEu@o@CCMIMKGEYSeAq@OKa@WGG[Sy@g@w@k@s@e@GEGE??YQgAu@c@[QKc@YMKKG@EBK@EVo@@EDKDM@Cb@cAFOBIt@oBBEDUJFBBFDx@l@??",
"arrival": "2026-10-23T09:31:53-04:00",
"arrival_epoch": 1792762313,
"distance_meters": 1840,
"duration_seconds": 245
},
{
"type": "job",
"id": "order-1002",
"sequence": 2,
"reachable": true,
"leg_distance_meters": 3108,
"leg_duration_seconds": 390,
"leg_geometry": "elwtGl}|_Mx@n@BBFD\\ZDDDBz@l@`@\\~BdB~AjAf@\\DDBBLDEPEJe@zAADENGNCFW|@Y|@CFA@ELCHGRq@jBABu@tBCHAFEJGNADg@vAKXq@lBABEHAHgA|C_@fASf@ADGPO^JHbAt@`CrBBBJHLJhCzBJL~BvBFDJJHF~@r@zAdABBHFLHdDfCBBFFFDJHvDlCRJDBRHXPx@f@D@HHJFVV|@x@BBLJTRJJTTlBdBLL`@\\h@f@ZXNLr@p@BBB@JJJHv@r@j@h@DDNLJJp@l@HHj@j@DBLLRRfB`BBBJJHFv@r@x@t@B@HHJJz@v@RRl@j@BBHHJHhAfAj@j@BBNLHHLLVT|@z@HFIPw@tBmAbDy@vBe@pACDIPIRg@pAO\\{A`ECDAFEJHHp@p@??",
"arrival": "2026-10-23T09:43:23-04:00",
"arrival_epoch": 1792763003,
"distance_meters": 4948,
"duration_seconds": 635
},
{
"type": "end",
"reachable": true,
"leg_distance_meters": 1468,
"leg_duration_seconds": 197,
"leg_geometry": "{cutG|wa`Md@f@LJFDDDFBBBFBDKBGrAkDFQBGp@eB@EFSFSBCb@mAl@}AjA{Cb@iAd@mABEFQJ[IG{ByBIGGIMKIKo@m@OMGI[Yc@a@e@c@m@m@ECIIKKaA}@EEu@q@CCIIKIq@o@{@y@ECIIII_@[[Y_@_@ECWWQOIIGE]]QO_@]CCKKYWKIu@q@MMYWCCKKKIACoAiAuAoAKIs@q@eA_AKKCCOOJO@A??",
"arrival": "2026-10-23T09:51:40-04:00",
"arrival_epoch": 1792763500,
"distance_meters": 6416,
"duration_seconds": 832
}
]
}
],
"summary": {
"routes": 1,
"tasks": 2,
"distance_meters": 6416,
"duration_seconds": 832,
"unreachable_legs": 0,
"violations": 0
}
},
"meta": {
"region": "NA",
"profile": "car",
"geometry": true,
"geometry_format": "polyline",
"departed_at": "2026-10-23T09:27:48-04:00",
"mode": "sync"
}
}
What it costs
Every point the solver must visit or depart from, including each vehicle's start and end.
Every billable response carries a billing block with the unit, the quantity and how the quantity was derived.
Errors it can return
| Status | Code | Description |
|---|---|---|
| 401 | TOKEN_INVALID |
Unknown or revoked API token. |
| 401 | TOKEN_MISSING |
No API token. Send Authorization: Bearer <your token>. |
| 403 | CAPABILITY_NOT_ENABLED |
This capability is not enabled for your account. See :doc to enable it. |
| 403 | CAPABILITY_NOT_GRANTED_TO_KEY |
Your account has this capability, but this API key was not granted it. |
| 403 | OPEN_API_DISABLED |
Open Platform access is not enabled for this account. |
| 422 | VALIDATION_FAILED |
The request failed validation. See details. |
| 429 | RATE_LIMITED |
Too many requests. Please retry after the indicated delay. |
| 500 | INTERNAL_ERROR |
An unexpected error occurred. Quote the request_id when contacting support. |