Java SDK
Java SDK
The official io.navvo:navvo-java library is a server-side Java client for the
Navvo Web Service. It is fully typed, retry-aware, thread-safe, and built on pure
JDK 11 (java.net.http.HttpClient) with a small vendored JSON parser — zero
third-party dependencies (no Jackson, no Guava). It is Kotlin-friendly and offers
both synchronous methods and CompletableFuture async mirrors. There is no map UI
or tile rendering — to render a map in the browser use
Navvo Maps.
Requires: Java 11+ (works on 11 / 17 / 21) · Base URL: https://navvo.io/api/v1
Install
<dependency>
<groupId>io.navvo</groupId>
<artifactId>navvo-java</artifactId>
<version>0.1.0</version>
</dependency>
implementation 'io.navvo:navvo-java:0.1.0'
import io.navvo.NavvoClient;
import io.navvo.model.*;
Authenticate
Every call is sent with the x-api-key header. Pass the key explicitly, or use
NavvoClient.fromEnv() to read it from the NAVVO_API_KEY environment variable. The
key shape (nvvo_<48 hex>) is validated up front, and the key is never logged — it is
redacted to nvvo_****<last4> everywhere.
NavvoClient navvo = NavvoClient.fromEnv(); // uses $NAVVO_API_KEY
NavvoClient navvo = NavvoClient.builder()
.apiKey("nvvo_your_key_here")
.build();
The SDK never puts the key in a URL — except styleUrl(), which builds a
browser-fetchable map style URL where the key must ride
as ?api_key= (browsers cannot set headers on tile/style requests).
Quickstart
import io.navvo.NavvoClient;
import io.navvo.model.*;
import java.util.*;
public class Demo {
public static void main(String[] args) {
try (NavvoClient navvo = NavvoClient.fromEnv()) {
// Forward geocode — string coordinates are parsed to numbers for you.
List<GeocodeResult> hits = navvo.geocode(
GeocodeQuery.builder().q("Abdali Hospital, Amman").limit(5).build());
GeocodeResult top = hits.get(0);
System.out.println(top.lat() + "," + top.lng() + " " + top.displayName());
// Plan a route — you pass LatLng(lat, lng); the SDK serialises lon/lat on the wire.
RoutePlan plan = navvo.route(RoutePlanRequest.builder()
.addLocation(LatLng.named(31.95, 35.91, "A"))
.addLocation(LatLng.named(31.99, 35.95, "B"))
.costing("auto")
.build());
System.out.println("ETA seconds: " + plan.durationSeconds());
// Decode the leg polyline (polyline6) into points.
List<LatLng> line = plan.geometry();
System.out.println("shape points: " + line.size());
// Live, condition-aware extras (NavvoObject with a raw() escape hatch).
NavvoObject wx = navvo.weatherPoint(31.95, 35.91, "metric");
System.out.println(wx.raw());
}
}
}
Configuration
Configure through the builder:
NavvoClient navvo = NavvoClient.builder()
.apiKey(System.getenv("NAVVO_API_KEY"))
.baseUrl("https://navvo.io/api/v1")
.timeout(java.time.Duration.ofSeconds(15))
.connectTimeout(java.time.Duration.ofSeconds(5))
.userAgent("acme-dispatch/2.3")
.retry(RetryConfig.builder()
.retries(3)
.baseDelay(java.time.Duration.ofMillis(500))
.maxDelay(java.time.Duration.ofSeconds(20))
.factor(2).jitter(true).respectRetryAfter(true)
.build())
.build();
Use navvo.withApiKey(key) to clone a client for a different key (multi-tenant
servers). A NavvoClient is immutable and safe for concurrent use across threads. The
client is AutoCloseable for try-with-resources.
Coordinates — never backwards
You always supply points as LatLng.of(lat, lng) (or LatLng.named(lat, lng, name)
for a waypoint). The SDK owns every wire-format difference: {lon, lat} for
route/trafficRoute, {lat, lon} for the routing passthrough payloads
(simpleRoute/matrix/optimizeStops), separate lat/lon params for reverse
geocode, west,south,east,north for BBox, [lng, lat] for GeoJSON, and WKT
POINT(lng lat) for a place center (parse it with place.centerLatLng()).
LatLng amman = LatLng.of(31.9539, 35.9106);
BBox box = BBox.fromCorners(35.85, 31.92, 35.98, 32.02); // west, south, east, north
Retries & errors
Transient failures (5xx, 408, 429/quota, network blips, and available:false
envelopes) are retried with exponential backoff and full jitter, honouring
Retry-After; client errors (400/401/403/404) are never retried. Every error
is an unchecked subtype of NavvoException:
try {
navvo.geocode(GeocodeQuery.of("Abdali Hospital, Amman"));
} catch (NavvoRateLimitException e) {
log.warn("rate limited, retry after {}", e.retryAfter());
} catch (NavvoScopeException e) {
log.error("scope/quota: {} (missing {})", e.reason(), e.scope());
} catch (NavvoServiceUnavailableException e) {
log.error("geocoder upstream is down — fall back");
}
The geocoder/routing envelope ({ provider, available, data }) is unwrapped for
you — an available:false body becomes a NavvoServiceUnavailableException — and an
unknown place slug returns an empty Optional (a soft-null, not an error).
| Exception | From |
|---|---|
NavvoConfigException | bad/missing key, bad option, non-HTTPS base URL |
NavvoValidationException | 400 (and other non-retryable 4xx) |
NavvoAuthException | 401 |
NavvoScopeException | 403 (has reason(), scope()) |
NavvoNotFoundException | 404 |
NavvoRateLimitException | 429 (has retryAfter(), limit(), remaining(), resetAt()) |
NavvoServiceUnavailableException | available:false, 502/503 |
NavvoServerException | other 5xx |
NavvoTimeoutException / NavvoNetworkException | timeout / transport |
The API key is sent only in the x-api-key header and never appears in an error;
errors carry redactedKey() (nvvo_****<last4>).
Method map
| Area | Methods |
|---|---|
| Tiles & config | styles, styleUrl, mapConfig |
| Search & geocoding | search, geocode, reverseGeocode, geocodeBatch, reverseGeocodeBatch |
| Places | places, placesStream, place, placeCategories, placeFacets, placeThumb, placeHours, placePopularTimes, placeAttributes, placeReviewsSummary, placePhotos, placeNearby, placesReachable |
| Routing | route, simpleRoute, matrix, isochrone, mapMatch, optimizeStops, optimize, routingProfiles |
| Traffic & incidents | trafficRoute, trafficSummary, incidents, incidentTypes, reportIncident, roadEdits, routeRisk |
| Weather & air quality | weatherPoint, weatherForecast, weatherCities, airQualityCities |
| EV & transit | evStations, transitStops, gbfs |
Each method maps to the endpoint documented in the
API reference. Typed results expose raw() with the full untyped
body for forward compatibility; less-structured responses return a NavvoObject whose
raw() returns the parsed tree. optimize requires the opt-in optimize scope;
trafficRoute requires both routing and traffic. See
Route Optimization and
Traffic & Incidents.
Pagination & async
GET /places is paginated; placesStream(query) walks every page lazily:
navvo.placesStream(PlaceListQuery.builder().category("restaurants").limit(100).build())
.forEach(p -> System.out.println(p.nameEn()));
Key methods have async mirrors returning CompletableFuture:
navvo.geocodeAsync(GeocodeQuery.of("Amman"));
navvo.routeAsync(req);
navvo.placeAsync("jordan-beans-cafe");
Building without Maven
The library compiles with the JDK alone:
javac --release 11 -d build/classes $(find src/main/java -name '*.java')
jar --create --file navvo-java-0.1.0.jar -C build/classes .
java -cp navvo-java-0.1.0.jar io.navvo.examples.Main # offline smoke test
A pom.xml is included for publishing to Maven Central (mvn clean package produces
the binary, -sources, and -javadoc jars).
Publishing & versioning
The artifact is published to Maven Central. The current version is 0.1.0:
<dependency>
<groupId>io.navvo</groupId>
<artifactId>navvo-java</artifactId>
<version>0.1.0</version>
</dependency>