Skip to main content

Choose the right API for the query type

Apple Maps exposes three ways to resolve a location query, and picking the right one affects both result quality and quota consumption.
When you have a well-formed street address, use api.geocode(). It is optimized for exact or near-exact address strings and returns precise coordinates and structured address components.
PlaceResults results = api.geocode(
    GeocodeInput.builder("880 Harrison St, San Francisco, CA 94107").build()
);
Geocoding a vague or fuzzy string (e.g., just a business name) produces poor results. Use Search or Autocomplete instead.
For incremental input (e.g., a search-as-you-type UI), use api.autocomplete(). It returns fast, lightweight completions without fetching full place details.
SearchAutocompleteResponse response = api.autocomplete(
    SearchAutocompleteInput.builder("Apple Par").build()
);
To get full place details for a selected suggestion, call api.resolveCompletionUrl(result.completionUrl()).

Always provide a location hint when possible

For name-only or ambiguous queries, passing a userLocation significantly improves result relevance. Without it, Apple Maps has no geographic context for ranking results.
SearchResponse response = api.search(
    SearchInput.builder("Stripe")
        .userLocation(UserLocation.fromLatitudeLongitude(37.7796095, -122.4016725))
        .build()
);
UserLocation.fromLatitudeLongitude() accepts decimal degrees. Pass the user’s last known position whenever it is available. The same .userLocation() builder method is available on SearchInput, SearchAutocompleteInput, and GeocodeInput. For DirectionsInput, use .userLocation(RouteLocation) — it accepts a RouteLocation rather than a UserLocation.

Use try-with-resources to manage the client lifecycle

AppleMaps implements AutoCloseable and manages an underlying HttpClient. Use try-with-resources to ensure the client is closed and its resources are released when you are done.
try (AppleMaps api = new AppleMaps(token)) {
    PlaceResults results = api.geocode(
        GeocodeInput.builder("Jungfernstieg 1").build()
    );
    // work with results
}
// api.close() is called automatically
If you are creating one long-lived instance per application (e.g., as a Spring bean), close it in a lifecycle shutdown hook instead.

Load the token from environment variables

Never hardcode your authorization token in source code. Load it from an environment variable or JVM system property:
String token = System.getenv("APPLE_MAPS_TOKEN");
if (token == null || token.isBlank()) {
    token = System.getProperty("APPLE_MAPS_TOKEN");
}
if (token == null || token.isBlank()) {
    throw new IllegalStateException(
        "Set APPLE_MAPS_TOKEN as an environment variable or JVM system property."
    );
}
If your JWT was issued with a specific origin claim, supply it via the two-argument constructor:
String origin = System.getenv("APPLE_MAPS_ORIGIN");
AppleMaps api = (origin == null || origin.isBlank())
    ? new AppleMaps(token)
    : new AppleMaps(token, origin);

Be aware of quota costs

Apple provides a daily service-call quota shared across your Apple Developer membership. Each call to geocode, reverseGeocode, autocomplete, search, or directions counts as one service call.
api.resolveCompletionUrls(response) makes one Search API call per completion result in parallel. If the autocomplete response contains 10 results, calling resolveCompletionUrls costs 10 additional service calls. Reserve this method for cases where you need full place details for every suggestion, such as a pre-fetching scenario. For a user clicking a single suggestion, use api.resolveCompletionUrl(result.completionUrl()) instead.
When you exceed the daily quota, the API responds with HTTP 429 Too Many Requests. To request additional capacity, contact Apple through the Maps developer dashboard.

Handle errors explicitly

The SDK throws two distinct runtime exception types. Catch them separately to distinguish between API-level failures and network-level failures.
try (AppleMaps api = new AppleMaps(token)) {
    PlaceResults results = api.geocode(
        GeocodeInput.builder("880 Harrison St, San Francisco, CA 94107").build()
    );
    // work with results
} catch (AppleMapsApiException e) {
    // The API returned a non-2xx HTTP response
    System.err.println("API error " + e.statusCode() + ": " + e.responseBody());
} catch (AppleMapsClientException e) {
    // A network or I/O failure occurred before a response was received
    System.err.println("Network error: " + e.getMessage());
}
ExceptionWhen thrownUseful fields
AppleMapsApiExceptionApple Maps Server returned a non-2xx responsestatusCode(), responseBody()
AppleMapsClientExceptionNetwork or I/O failuregetCause() for the underlying exception
Both are RuntimeException subclasses, so you are not required to declare them in method signatures. Catch them at the appropriate boundary (controller, use-case, or job handler) and apply your application’s retry or alerting policy.

Set a custom timeout

The default request timeout is 10 seconds. Pass a Duration to the constructor to override it:
AppleMaps api = new AppleMaps(token, Duration.ofSeconds(30));
Choose a timeout that reflects your latency requirements. Geocoding and search calls typically complete well under 1 second under normal conditions; a 5–15 second timeout is reasonable for most production workloads.