Java

Mapricot's API is plain HTTPS and JSON, so Java needs no Mapricot library to call it. This page uses Java's built-in HttpClient and Jackson for JSON to call each API: geocoding, places, routing, and IP geolocation, and shows where the free map tiles fit. It was tested with Java 21 and Jackson 2.22, and works with Java 11 and later.

Set up

Add Jackson to your build. With Maven:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.22.4</version>
</dependency>

With Gradle, add implementation("com.fasterxml.jackson.core:jackson-databind:2.22.4") to dependencies.

Create a server key in the dashboard with the products you'll use: Places for geocoding and places, Routing, and IP geolocation. Set it as an environment variable, so it stays out of your code:

export MAPRICOT_KEY="mapricot_sk_…"

Then add get and post methods that send the key with every request, retry when the API is busy, and throw on errors. They return the response as a Jackson JsonNode. The examples below go in main:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Mapricot {
    static final HttpClient client = HttpClient.newHttpClient();
    static final ObjectMapper json = new ObjectMapper();

    static JsonNode get(String path, Map<String, Object> params) throws IOException, InterruptedException {
        String query = params.entrySet().stream()
                .map(e -> e.getKey() + "=" + URLEncoder.encode(String.valueOf(e.getValue()), StandardCharsets.UTF_8))
                .collect(Collectors.joining("&"));
        String url = "https://api.mapricot.com" + path + (query.isEmpty() ? "" : "?" + query);
        return send(HttpRequest.newBuilder(URI.create(url)).GET());
    }

    static JsonNode post(String path, Object body) throws IOException, InterruptedException {
        return send(HttpRequest.newBuilder(URI.create("https://api.mapricot.com" + path))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(json.writeValueAsString(body))));
    }

    static JsonNode send(HttpRequest.Builder builder) throws IOException, InterruptedException {
        HttpRequest request = builder
                .header("Authorization", "Bearer " + System.getenv("MAPRICOT_KEY"))
                .timeout(Duration.ofSeconds(30))
                .build();
        for (int attempt = 1; ; attempt++) {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            int status = response.statusCode();
            if ((status == 429 || status == 503) && attempt < 4) {
                Thread.sleep(1000L << attempt);
                continue;
            }
            JsonNode body = json.readTree(response.body());
            if (status >= 400) {
                throw new IOException("Mapricot returned " + status + ": " + body.at("/error/message").asText());
            }
            return body;
        }
    }

    public static void main(String[] args) throws Exception {
        // The examples go here.
    }
}

Geocode an address

/v1/search turns an address or place name into coordinates:

JsonNode search = get("/v1/search", Map.of("q", "128 SW 3rd Ave, Portland", "limit", 1));
for (JsonNode result : search.get("results")) {
    System.out.println(result.get("label").asText() + " " + result.get("lat").asDouble() + " " + result.get("lon").asDouble());
}
128 SW 3rd Avenue, Portland, OR, USA 45.521736 -122.673276

Each result also has its address in parts under address, and a type, such as address, street, or locality, that says how exact the match is. To limit results to some countries, add "country", "US,CA". Geocoding lists every parameter and field.

Find the address at a point

/v1/reverse turns coordinates into the nearest address:

JsonNode nearest = get("/v1/reverse", Map.of("lat", 45.5217, "lon", -122.6733, "limit", 1)).get("results").get(0);
System.out.println(nearest.get("label").asText() + " " + nearest.get("distanceMeters").asInt());
128 SW 3rd Avenue, Portland, OR, USA 4

Suggest places as someone types

/v1/autocomplete takes the text typed so far. focus ranks results near a point first:

JsonNode suggestion = get("/v1/autocomplete", Map.of("q", "stumpt", "focus", "45.52,-122.68", "limit", 1)).get("results").get(0);
System.out.println(suggestion.get("label").asText() + " " + suggestion.get("id").asText());
Stumptown Coffee Roasters, Portland, OR, USA mp_b3BlbnN0cmVldG1hcDp2ZW51ZTpub2RlLzExOTg2MjI2NjYw

Find places nearby

/v1/nearby lists places of a category around a point, nearest first:

JsonNode nearby = get("/v1/nearby", Map.of("lat", 45.52, "lon", -122.68, "category", "food:coffee_shop", "radius", 1000, "limit", 1));
for (JsonNode place : nearby.get("results")) {
    System.out.println(place.get("name").asText() + " " + place.get("distanceMeters").asInt());
}
Public Domain 37

Look up a place by ID

Every result has an id. Save it, and /v1/places/{id} fetches that place again later:

JsonNode place = get("/v1/places/" + suggestion.get("id").asText(), Map.of()).get("result");
System.out.println(place.get("label").asText());
Stumptown Coffee Roasters, Portland, OR, USA

Get a route

/v1/route takes a JSON body with two to 20 waypoints and a mode of drive, walk, or bicycle:

JsonNode route = post("/v1/route", Map.of(
        "waypoints", List.of(Map.of("lat", 43.7396, "lon", 7.4275), Map.of("lat", 43.734, "lon", 7.4222)),
        "mode", "walk")).get("routes").get(0);
System.out.println(route.get("distanceMeters").asInt() + " " + route.get("durationSeconds").asInt());
1044 739

Distances are in meters and times in seconds. When no path connects the waypoints, routes is empty. The route's line is in polyline, encoded with six decimal places, so decode it with precision 6, as decoding a route polyline explains.

Look up an IP address

/v1/ip/{ip} gives the approximate location of an IPv4 or IPv6 address:

JsonNode location = get("/v1/ip/8.8.8.8", Map.of());
System.out.println(location.get("city").asText() + " " + location.get("countryCode").asText());
Mountain View US

Show a map

Map tiles are free and need no key. MapLibre GL JS draws them in the browser from https://tiles.mapricot.com/styles/light.json, so Java on a server doesn't fetch them: send your results to a web page, and draw them there, as Map tiles shows. MapLibre takes coordinates as [lon, lat]. For an Android app, see Kotlin.

Errors and retries

send throws an IOException for any error response, with the status and the API's message:

try {
    get("/v1/search", Map.of("q", ""));
} catch (IOException error) {
    System.out.println(error.getMessage());
}
Mapricot returned 400: The service rejected the request.

It retries 429 and 503 responses up to three times, waiting 2, 4, then 8 seconds. Errors aren't billed, so retrying them costs nothing. A 429 that keeps coming back can also mean the account is out of credit. Each account has a limit on requests in progress at once, so send requests one at a time or a few at once. The API reference lists every status.