Documentation menu
POST/v1/ev/plan
API key required, sent as Authorization: Bearer snk_… or ?api_key=snk_….
What it does
The algorithm
- Base route. One
autoroute over origin + waypoints + destination, with the request'scosting_optionsverbatim. - Leg sampling. Each manoeuvre of that route becomes one
sn_ev::LegSample: its length and its own average speed, which is finer than a leg summary and so makes the steady-state assumption insn_ev::consumption_over_legsbetter. Manoeuvre-free routes fall back to one sample per leg, and a route with more thanMAX_LEG_SAMPLESmanoeuvres has consecutive ones coalesced. - Elevation, or an honest absence. When elevation is enabled the
gateway's own height service is asked for the elevation at every leg
boundary, and each leg's gain and loss come from its two ends. When
it is not enabled, or the DEM has no coverage, the legs are built
with
sn_ev::LegSample::without_gradient, the consumption model runs flat (the only thing it can do) and the response saysgradient_data: "absent". A gradient is never silently assumed to be zero. - Direct plan.
sn_ev::consumption_over_legsover the whole route fromstart_soc. If the arrival state of charge is at or abovemin_arrival_socand the reserve was never breached, that is the plan: no charger work and no matrix call happen at all. - Candidates. Otherwise, charge points from the store are projected
onto the route (
corridor::nearest_on_route), filtered to those the vehicle can plug into at a rated power inside the corridor budget, and capped atMAX_PLAN_CANDIDATESspread along the route rather than clustered; the/v1/charging/alongcorridor machinery, with a spread instead of a power ranking, because a planner needs reach and a search needs kilowatts. - One matrix. Origin + candidates → candidates + destination, one
(n+1)²matrix through the gateway's own engine with the request's costing options, giving every hop a real driving time and distance: never a crow-flies guess. - Greedy with lookahead.
crate::ev::plan_stopswalks the route choosing stops so the state of charge never breachesreserve_soc, preferring a stop that finishes the journey, then the furthest one (fewer stops), then the cheapest by driving plus charging time. A charger from which nothing further can be reached even on a full battery is refused rather than taken. At most eight passes. - Final geometry. The chosen stops are merged with the waypoints in route order and the route is recomputed through them, so the geometry returned is the journey actually described.
- Or an honest no. When no plan exists (a charger desert, a
connector mismatch, a gap wider than the vehicle's range), the answer
is a 200 with
feasible: false, the named cause, and the furthest point on the route the vehicle can actually reach. Never a fabricated plan.
Stated limitations
A hop's energy is sliced out of the base route's legs and the detour
surplus charged as one gradient-unknown leg (see
crate::ev::hop_legs); matrix cells are upper bounds on route cost,
not route costs; the consumption model's own ±15 % motorway bound and
its urban bias are stated in sn_ev; charging curves are best-case,
measured on a warm pack. The plan is a plan, not a promise.
Call it
curl -fsS -X POST "https://api.mapmap.ai/v1/ev/plan" \
-H "Authorization: Bearer $MAPMAP_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonRequest body
application/json
EV journey plan: origin + destination (+ optional waypoints), a vehicle (profile: "small_hatch" | "saloon" | "suv" | "van", or an inline object of EvProfile fields over a base), start_soc (default 0.9), min_arrival_soc (default 0.1), reserve_soc (default 0.1, the mid-route floor), a connectors filter, min_kw, ambient_temperature_c, max_detour_minutes (default 15, max 120) and Valhalla costing_options (the costing is always auto)
The OpenAPI document declares this body as a free-form JSON object with no field list, so there is nothing here to generate a table from. The prose above is what the gateway states about it; the raw document is the authority.
Responses
{feasible, plan: {stops, total_drive_s, total_charge_s, total_duration_s, total_distance_m, start_soc, arrival_soc, min_arrival_soc, reserve_soc, energy_kwh}, stops[]: {charger_id, name, operator, source, lat, lon, connector: {standard, power_kw, dc, power_kw_source; derived when the feed did not publish the rating}, charger_kw, arrive_soc, depart_soc, charge_s, detour_s, along_route_position, off_route_m, status, status_live}, legs[]: {from, to, duration_s, distance_m, start_soc, end_soc, min_soc, consumed_wh, regen_wh}, soc_trace[], gradient_data: "complete" | "partial" | "absent", gradient: {legs_total, legs_with_gradient, fraction}, route: {duration_s, distance_m, length_m}, geometry_polyline6, coverage_note (ALWAYS present), sources[], availability: {live, note}, provenance: {profile, availability, consumption_model, charge_model}, charging_attribution}. When feasible is false the answer carries reason: {code, message} and furthest_reachable, never a fabricated stop listError bodies follow the shared problem model documented on API conventions.
Also under routing
Generated from https://api.mapmap.ai/openapi.json on 2026-09-17 · operationId post_ev_plan