Go SDK
Go SDK
The official github.com/navvo/navvo-go module (package navvo) is a
server-side Go client for the Navvo Web Service. It is fully typed,
retry-aware, concurrency-safe, and uses only the standard library
(net/http, encoding/json, context) — no third-party dependencies.
Every call takes a context.Context first argument. There is no map UI or
tile rendering — for rendering a map in the browser use
Navvo Maps.
Requires: Go 1.21+ · Base URL: https://navvo.io/api/v1
Install
go get github.com/navvo/navvo-go
import navvo "github.com/navvo/navvo-go"
Authenticate
Every call is sent with the x-api-key header. Pass the key explicitly, or pass
"" to read it from the NAVVO_API_KEY environment variable. The key shape
(nvvo_<48 hex>) is validated up front.
c, err := navvo.New("") // uses $NAVVO_API_KEY
c, err := navvo.New("nvvo_your_key_here") // explicit
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
package main
import (
"context"
"fmt"
"log"
"time"
navvo "github.com/navvo/navvo-go"
)
func main() {
c, err := navvo.New("") // reads NAVVO_API_KEY
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Forward geocode — string coordinates are parsed to numbers for you.
hits, err := c.Geocode(ctx, "Abdali Hospital, Amman", &navvo.GeocodeOptions{Limit: 5})
if err != nil {
log.Fatal(err)
}
fmt.Println(hits[0].Lat, hits[0].Lng, hits[0].DisplayName)
// Plan a route — you pass {Lat, Lng}; the SDK serialises lon/lat on the wire.
plan, err := c.Route(ctx, navvo.RoutePlanRequest{
Locations: []navvo.LatLng{
{Lat: 31.95, Lng: 35.91, Name: "A"},
{Lat: 31.99, Lng: 35.95, Name: "B"},
},
Costing: "auto",
})
if err != nil {
log.Fatal(err)
}
fmt.Println("ETA seconds:", plan.Trip().Summary.Time)
// Decode the leg polyline (polyline6) into points.
line := plan.Geometry()
fmt.Println("shape points:", len(line))
// Live, condition-aware extras.
wx, _ := c.WeatherPoint(ctx, 31.95, 35.91, "metric")
fmt.Println(wx.Temperature, wx.Unit, "·", *wx.AQICategory)
}
Configuration
New accepts functional options:
c, err := navvo.New(
"",
navvo.WithBaseURL("https://navvo.io/api/v1"),
navvo.WithTimeout(15*time.Second),
navvo.WithUserAgent("acme-dispatch/2.3"),
navvo.WithRetry(navvo.RetryConfig{
Retries: 3, BaseDelay: 500 * time.Millisecond, Factor: 2, Jitter: true, MaxDelay: 20 * time.Second,
}),
)
Use navvo.WithHTTPClient(myClient) to bring your own pooled *http.Client, and
c.WithAPIKey(key) to clone a client for a different key (multi-tenant servers).
A *Client is safe for concurrent use across goroutines.
Coordinates — never backwards
You always supply points as navvo.LatLng{Lat, Lng}. The SDK owns every
wire-format difference: {lon, lat} for Route/TrafficRoute, {lat, lon} for
the routing passthrough payloads (SimpleRoute/Matrix/Optimize), separate
lat/lon params for reverse geocode, west,south,east,north for BBox,
[lng, lat] for GeoJSON, and WKT POINT(lng lat) for place center (parse it
with navvo.ParseWKTPoint(place.Center) or place.CenterLatLng()).
amman := navvo.LatLng{Lat: 31.9539, Lng: 35.9106}
box := navvo.BBox{West: 35.85, South: 31.92, East: 35.98, North: 32.02}
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 wraps *navvo.APIError and supports errors.Is /
errors.As:
hits, err := c.Geocode(ctx, "Abdali Hospital, Amman", nil)
if err != nil {
var rl *navvo.RateLimitError
var scope *navvo.ScopeError
switch {
case errors.As(err, &rl):
log.Printf("rate limited, retry after %v", rl.RetryAfter)
case errors.As(err, &scope):
log.Printf("scope/quota problem: %s (missing %s)", scope.Reason, scope.Scope)
case errors.Is(err, navvo.ErrServiceUnavailable):
log.Print("geocoder upstream is down — fall back")
default:
return err
}
}
The geocoder/routing envelope ({ provider, available, data }) is unwrapped
for you — an available:false body becomes a *navvo.ServiceUnavailableError —
and an unknown place slug returns nil (a soft-null, not an error). Cancel or
deadline any call through the context.Context you pass.
| Error | Sentinel | From |
|---|---|---|
*ConfigError | ErrConfig | bad/missing key, bad option |
*ValidationError | ErrValidation | 400 (has Fields) |
*AuthError | ErrUnauthorized | 401 |
*ScopeError | ErrForbidden | 403 (has Reason, Scope) |
*NotFoundError | ErrNotFound | 404 |
*RateLimitError | ErrRateLimited | 429 (has RetryAfter) |
*ServiceUnavailableError | ErrServiceUnavailable | available:false, 502/503 |
*ServerError | ErrServer | other 5xx |
*TimeoutError / *NetworkError | ErrTimeout / ErrNetwork | 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 |
| Places | ListPlaces, Place, PlaceThumb, PlaceCategories |
| Routing | Route, SimpleRoute, Matrix, 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. Results expose a Raw field
(map[string]any) with the full untyped body for forward compatibility.
Optimize requires the opt-in optimize scope; TrafficRoute requires both
routing and traffic. See Route Optimization
and Traffic & Incidents.
Publishing & versioning
The module is published via Git tags. The current version is v0.1.0:
go get github.com/navvo/navvo-go@v0.1.0