Navvo
Guides

Java SDK

Install and use the official io.navvo:navvo-java client for server-side geocoding, routing, places, traffic, EV, transit, and more.

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>
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).

ExceptionFrom
NavvoConfigExceptionbad/missing key, bad option, non-HTTPS base URL
NavvoValidationException400 (and other non-retryable 4xx)
NavvoAuthException401
NavvoScopeException403 (has reason(), scope())
NavvoNotFoundException404
NavvoRateLimitException429 (has retryAfter(), limit(), remaining(), resetAt())
NavvoServiceUnavailableExceptionavailable:false, 502/503
NavvoServerExceptionother 5xx
NavvoTimeoutException / NavvoNetworkExceptiontimeout / 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

AreaMethods
Tiles & configstyles, styleUrl, mapConfig
Search & geocodingsearch, geocode, reverseGeocode, geocodeBatch, reverseGeocodeBatch
Placesplaces, placesStream, place, placeCategories, placeFacets, placeThumb, placeHours, placePopularTimes, placeAttributes, placeReviewsSummary, placePhotos, placeNearby, placesReachable
Routingroute, simpleRoute, matrix, isochrone, mapMatch, optimizeStops, optimize, routingProfiles
Traffic & incidentstrafficRoute, trafficSummary, incidents, incidentTypes, reportIncident, roadEdits, routeRisk
Weather & air qualityweatherPoint, weatherForecast, weatherCities, airQualityCities
EV & transitevStations, 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>
Copyright © 2026