Build smarter routing into your product. One endpoint, conditional data layers, predictable responses.
The Viscar Developer API is a JSON-over-HTTPS interface for route planning enriched with traffic-control signs, turn-by-turn maneuvers, ETA-aware weather forecasts and per-segment road-quality scoring. Every endpoint lives under a versioned path so existing integrations stay stable as the platform evolves.
All requests and responses use UTF-8 JSON. Coordinates are decimal degrees (WGS84). Distances are in meters, durations in seconds - SI units throughout the public contract.
Every request needs an API key. Generate one in your Dashboard after creating an account. Keys start with the prefix vsk_live_.
Pass the key in either header - both are accepted, pick whichever fits your client better:
Requests without a valid key return 401 Unauthorized with an error envelope. Keep keys server-side - never ship them in a mobile bundle or web frontend you don't fully control.
Every API key is tied to a plan. Plans differ in daily request quota and the number of API keys you can issue. Your current plan and remaining quota are shown in your dashboard - see pricing for the current product tiers and what each plan includes.
| Plan | Requests / day | API keys |
|---|---|---|
free | 250 | 1 |
developer | 1 500 | 5 |
startup | 8 000 | 20 |
enterprise | 50 000 | unlimited |
Exceeding your daily quota returns 429 Too Many Requests. In V1 every public endpoint is reachable from the Free tier - higher plans raise the quota; they do not unlock additional endpoints. Endpoint-level plan gating will appear when future tiers introduce restricted features; until then every endpoint carries the ALL PLANS badge.
An additional, orthogonal axis is the user role (set in your account, separate from your billing plan): an admin user's API keys bypass plan-tier gates and the daily quota - so internal tooling and support stay reachable without forcing the team onto a higher tier.
Beta note: paid plans are visible but not purchasable yet - pricing and access policy is pending review with legal counsel. The Free tier is fully operational; upgrade flows will open after launch.
The API version sits in the URL path (/dev/v1/...). When a breaking change is unavoidable a new version is published alongside the existing one - older versions keep working for a deprecation window of at least 12 months.
Within a major version we add fields but never rename or remove them. Clients should ignore unknown response fields to stay forward-compatible.
Computes a route between two coordinates and returns all data layers by default. Drop the cost-bearing optional layers you don't need (weather, quality) by listing them in exclude.
Omit exclude (or send []) to receive the full response. The example above keeps weather and drops quality.
| Field | Type | Required | Description |
|---|---|---|---|
origin | object {lat, lon} | yes | Start coordinate. lat ∈ [-90, 90], lon ∈ [-180, 180]. |
destination | object {lat, lon} | yes | End coordinate. Same range rules as origin. |
exclude | string[] | no | Optional layers to drop (opt-out). Allowed values: weather, quality. All layers are returned by default; omit or send [] for the full response. The always-on core (summary, geometry, maneuvers, signs) cannot be excluded - putting any of those names here yields 400. |
avoid | string[] | no | Road classes to avoid. Allowed: highways, tolls. |
profile | string | no | Vehicle profile. Currently only car is supported. |
course | int | no | Driver's heading at origin in degrees from true north (0..360 inclusive). Tells the router not to suggest an immediate U-turn when the vehicle is already moving. Omit (or send 0) when the vehicle is stationary or heading is unknown. |
Every successful response carries the always-on core (request, summary, segments, maneuvers, signs, attribution) plus the optional blocks (weather, quality) - all present by default. Any block you list in exclude is omitted from the JSON entirely.
Segments are the structural backbone - each carries its own coordinate array, length, duration and max-speed. Maneuvers, signs and quality all reference segments by sid.
Echoes back the validated, normalized parameters that produced this response. Lets you confirm how the server interpreted your input and reconstruct the call from a logged response.
Route totals + segment count for quick skim before iterating segments[].
The structural backbone of the response. Each item carries its own coordinate array, the length and duration of that piece of the drive, and the posted max-speed. Maneuvers live in their own top-level array (next section) and reference segments via from_sid / to_sid.
Segment geometries do not duplicate the shared junction point - each coordinate appears exactly once across the route, in driving order. Concatenating the geometry arrays segment-by-segment yields the full route polyline with no dedup step.
Coordinate order is [lon, lat] per RFC 7946 - wrap the array in a { "type": "LineString", "coordinates": … } object on the client if your mapping library wants a full GeoJSON geometry. max_speed_kmh stays in kilometers per hour because that's the unit the posted signs use; 0 means the underlying road has no speed limit known to the graph.
Top-level turn-by-turn instructions in driving order. Each item identifies the segments it connects with explicit from_sid + to_sid - joins onto the response's segments[] array without coordinate lookup.
type values| Type | Description |
|---|---|
straight | Continue straight through the junction. |
left | Turn left. |
right | Turn right. |
exit_left | Take the exit on the left (motorway / link). |
exit_right | Take the exit on the right (motorway / link). |
keep_left | Keep left where the road splits. |
keep_right | Keep right where the road splits. |
u_turn | Make a U-turn. |
roundabout_left | Enter a roundabout, overall direction left. |
roundabout_right | Enter a roundabout, overall direction right. |
roundabout_straight | Enter a roundabout, continuing straight. |
roundabout_u_turn | Enter a roundabout to reverse direction. |
roundabout_exit | Exit a roundabout; to_street carries the exit number and street. |
from_sid is 0 on the very first maneuver of a route (no inbound segment). For roundabout exits, to_street follows the format "Nth exit — Street Name".
Traffic-control elements and POIs along the route in driving order: stops, signals, give-way, crossings, speed cameras, mini-roundabouts, turning circles and loops, traffic calming, hazards and city limits. Pre-filtered for the actual driving direction (an inbound stop on a side street is shown; the same node on the through-road is suppressed). Each item carries the sid of the segment it belongs to.
type values| Type | Description |
|---|---|
stop | Stop sign. |
all_way_stop | All-way stop. |
traffic_signals | Traffic light. |
give_way | Yield / give-way. |
crossing | Pedestrian crossing (see crossing_type). |
mini_roundabout | Mini-roundabout. |
turning_circle | Widened turning circle at a road end. |
turning_loop | Turning loop (circular dead-end). |
traffic_calming | Traffic-calming feature (see calming_type). |
speed_camera | Speed / red-light camera (see cam_enforcement, cam_maxspeed). |
hazard | Road hazard (see hazard_type). |
city_limit | City-limit boundary. |
Optional sub-fields appear only when meaningful for the sign type: on_way_position (0..1 along the segment, for mid-segment signs like cameras), cam_enforcement + cam_maxspeed (cameras), crossing_type, hazard_type, calming_type.
Returned by default; drop it with "exclude": ["weather"]. ETA-aware weather forecast for points sampled along the route. Each point's reading is projected to the time the driver is expected to arrive there - not "now". Forecast horizon is 36 hours.
In-house risk alerts are included on every point: rain and snow intensity, slippery road, strong wind, glare, low visibility, high UV, tornado risk. Each alert carries a severity mark 1..4 (informational → extreme) and a value - the physical magnitude behind the alert in its natural unit (see below).
upon_arrival - a driver-focused summary of the forecast at the destination, projected to the arrival ETA: temperature, feels-like, humidity, wind & gusts, visibility, probability of precipitation and the condition (with rain_mm_h / snow_mm_h when present). Omitted when the weather layer is excluded or no forecast was available.
type values| Type | value | Description |
|---|---|---|
rain | mm/h | Rain intensity along the route. |
snow | mm/h | Snowfall rate. |
strong_wind | m/s | Strong wind (max of sustained and gust). |
slippery_road | 0 | Black ice, freezing rain or aquaplaning risk. |
glare_risk | 0 | Low-sun glare risk shortly after sunrise / before sunset under clear sky. |
low_visibility | meters | Reduced visibility (fog and similar). |
high_uv | UV index | High UV exposure. |
tornado | 0 | Severe convective / tornado risk. |
Where value is 0 there is no single physical magnitude for the alert - its importance is carried by mark.
mark (severity)| Mark | Description |
|---|---|
1 | Informational. |
2 | Moderate. |
3 | Severe. |
4 | Extreme. |
condition_main values| Value | Description |
|---|---|
Clear | Clear sky. |
Clouds | Cloud cover (few to overcast). |
Drizzle | Drizzle. |
Rain | Rain, including freezing rain. |
Snow | Snow. |
Fog | Fog. |
Thunderstorm | Thunderstorm. |
Tornado | Severe convective (mapped from extreme hail / convective). |
Returned by default; drop it with "exclude": ["quality"]. Three parallel arrays:
segments[] - per-SID aggregate scores. Join onto segments[] in the top-level response by sid.potholes[] - discrete point hits (pothole / sharp bump), each a single coordinate carrying its owning sid.anomalies[] - short bad stretches, each an ordered points[] list you draw a polyline through.Scores are a road-surface quality coefficient clamped to 0–2.0 - higher is smoother, with ≈1.0 as the reference baseline. See the colour scale below. Only segments with measured telemetry appear in segments[] - if a segment's sid from the top-level segments[] array is missing here, no quality data exists for it yet. potholes[] and anomalies[] are independent and may still carry items even when no per-segment row is returned for the surrounding sids.
| Colour | avg_score | Rating |
|---|---|---|
≥ 0.90 | Excellent | |
0.70 – 0.90 | Good | |
0.56 – 0.70 | Moderate | |
< 0.56 | Poor |
The same buckets the app uses to tint route segments - the green-to-red palette matches the live route overlay, and potholes[] / anomalies[] scores use the same 0–2.0 scale (lower = worse).
Viscar-derived from sensor analysis - not upstream-attributed.
Every successful response includes an attribution block. When required is true, displaying the attribution to your end users is a license obligation, not a UI preference - show the text field (or compose your own from providers[]) somewhere visible in your product.
| Provider | Triggered by | License | Why |
|---|---|---|---|
OpenStreetMap | segments | ODbL 1.0 | Underlying road network used to compute the polyline - the scope entry names the segments layer whose geometry is OSM-derived. Not triggered by maneuvers or signs alone - those are produced by Viscar's algorithms, even though their inputs originate in OSM data. |
Open-Meteo | weather | CC BY 4.0 | Forecast data shipped largely as-is from the upstream provider. |
required: falseOn the build endpoint required is always true - segments[] (OSM-derived geometry) is part of the always-on core and cannot be excluded. The false state is reserved for future endpoints returning only Viscar-original data: there providers is empty, the text field is omitted, and no attribution display is required.
Put it somewhere visible: the bottom-right corner of the map view, a small footer line below an itinerary panel, or a popover on a tap target.
Every failure response uses the same RFC 9457 application/problem+json envelope - request validation, routing, and API-key / quota / plan checks all share it. Branch on the machine code in the body, not on the HTTP status or the human-readable detail.
Returned as application/problem+json (RFC 9457) for 400 / 404 / 500. Branch on the machine code - a stable extension member - rather than on the HTTP status or the human-readable detail (which may evolve). status mirrors the HTTP status code.
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | Missing or malformed request body. |
| 400 | MISSING_ORIGIN | Field origin not provided. |
| 400 | MISSING_DESTINATION | Field destination not provided. |
| 400 | INVALID_COORDINATE | lat/lon out of range or non-finite. |
| 400 | INVALID_EXCLUDE_OPTION | Unknown value in exclude array, or an always-on layer listed there. |
| 400 | INVALID_AVOID_OPTION | Unknown value in avoid array. |
| 400 | INVALID_PROFILE | Unknown value in profile. |
| 400 | INVALID_COURSE | Field course not in [0, 360]. |
| 404 | ROUTE_NOT_FOUND | Origin and destination are valid but no drivable route connects them. |
| 404 | REGION_NOT_SUPPORTED | Origin or destination is outside the supported map area (no road within range). details.point is origin or destination. |
| 500 | ROUTE_BUILD_FAILED | Internal routing error. Retry once before escalating. |
| 500 | INTERNAL_ERROR | Unexpected failure. Safe to retry; if it persists, report it. |
Returned for 401 / 403 / 429 / 503 by the API-key / plan-gating layer, in the same problem+json shape - branch on code. Plan rejections add details.current_plan + details.required_plan.
| HTTP | Code | When |
|---|---|---|
| 401 | API_KEY_MISSING | No API key on a plan-gated endpoint. |
| 401 | API_KEY_INVALID | Key is malformed, unknown, disabled, or revoked. |
| 403 | PLAN_UPGRADE_REQUIRED | Plan tier below the endpoint's requirement. details carries current_plan + required_plan. |
| 429 | DAILY_LIMIT_EXCEEDED | Daily request quota for your plan exhausted. Resets daily. |
| 503 | AUTH_SERVICE_UNAVAILABLE | Transient - the auth service is unreachable. Retry with backoff. |