Skip to main content
V3 is in beta. General availability (GA) is planned for January 2027.
This page describes the V3 API (/v3/routing). For the V2 API (/v2/vrp), see the V2 version of this page.
The V3 Routing API is Solvice’s vehicle routing solver: send it jobs (and, for pickup-then-delivery work, shipments) plus a fleet of vehicles, and it returns optimised routes with arrival times, an itemised cost breakdown, and structured reasons for anything it couldn’t assign. It is the successor to the V2 VRP API, with named capacity dimensions and money-denominated costs everywhere. If you are coming from V2, see Migration from V2. Three routing endpoints make up the core of the API (see the Introduction for validation and runs): All three are served from https://api.solvice.io and take a JSON body with your API key in the Authorization header. Send a request to POST /v3/routing/validate/solve, /v3/routing/validate/evaluate or /v3/routing/validate/suggest to check it without solving it: it runs every check the real call runs before its travel-matrix fetch, then returns 200 {"valid": true, "warnings": []} or the same 400 (or 403) the real call would return. No matrix fetch, no search, not billed. Request bodies may be gzip-compressed (Content-Encoding: gzip); the 10 MiB limit applies after decompression. See Limits for the other caps. A problem of 401 to 10,000 tasks must be submitted as an async run, and a solve that should run longer than 60 seconds, or without holding a connection, can be: POST /v3/routing/runs takes the same body. This guide covers /v3/routing/solve, the one you’ll use first.

Your first solve

1

Get your API key

Sign up at platform.solvice.io and copy your API key from Settings → API Keys. See Authentication for the full flow.
2

Send a solve request

3

Read the response

The response carries a summary, one entry per non-empty routes[], the same routes as a plan, and any unassigned jobs with structured reasons. Stop times are instants in the shift’s own UTC offset (+02:00 here); distances and durations come from the road network. The figures below are illustrative: a live solve returns the road-network values for your coordinates.
How to read it:
  • Identity. routes[].vehicle and routes[].shift are the plain id strings from the request.
  • Stops. Each stop’s location is the coordinate you sent; snapped_location is the same point snapped to the road network, when available. arrival is before any waiting: the 1,637 s of wait_s at delivery-1 is the truck arriving before that job’s window opens. service_start is when service begins, after the wait: 09:00 at delivery-1 and 10:00 at delivery-2, the moments their windows open.
  • Load. delivery_load on a job is goods loaded at the depot and dropped off at the job, so load_after starts at 30 on the start stop and falls to 0. Use pickup_load on a job for goods that go onto the vehicle there, which makes load_after rise instead. See Jobs.
  • Durations. time.travel_s is driving time only. time.span_s is the route’s span from its start to its finish, including service and waiting.
  • Cost. This request set no objective, so the default rates apply: 0.20 per km of distance_m and 35 per hour of time.span_s. That gives the distance and time components above: 169.569 km × 0.20 = 33.9138, and 10,397 s × 0.00972 = 101.05884. They sum to score.cost. The hourly rate is applied per second at a resolution of 5 decimals, so 35 per hour is charged as 0.00972 per second. Expect the time component to come out a few cents under hours × 35.
  • Search time. timings.search_ms is 500 although time_limit_s is 5: a solve without a plan for one vehicle with one shift and time windows on its jobs caps its search at 500 ms below 60 visits, when time_limit_s is 6 or less. See Performance.
  • Score. Every entry of score.components is always present. score.currency is absent here because no objective.currency label was sent.
  • Coverage. tasks.unassigned is 0 because nothing is unassigned. If some jobs cannot be placed the call still returns 200, with tasks.unassigned above 0 and one unassigned[] entry per job explaining why.
  • Plan. plan carries the same routes in the request’s plan vocabulary. Feed it straight into evaluate, suggest, or a warm-started solve.
See The V3 API model for every response field, Objective function for the cost breakdown, and Solution quality for how to judge a plan.
Got a 400 instead? The body names the offending field in errors[].pointer. See Errors.

What’s different from V2

Next steps

The V3 API model

How the full request and response are structured: jobs, shipments, vehicles, relations, objective.

Field service day

A worked example: one day of service jobs solved, then a manual change scored with evaluate and a new job placed with suggest.

Constraints

Every field-level constraint the solver enforces, hard vs. soft.

Evaluate

Score an existing plan and list the rules it breaks.

Suggest

Rank where a new job fits into an existing plan.

Errors

The error body, every error code, and what to retry.

Limits

Request size, count, and time-limit caps.

Migration from V2

Map V2 concepts and fields onto V3.

API reference

Base URL, authentication, and the full schema of every endpoint.