IP Geolocation in Java & Spring Boot: Get Location from IP
Java remains one of the most widely used languages for backend services. Adding IP geolocation to your Java application enables location-based personalization, fraud detection, and compliance features. This guide covers plain Java, Spring Boot, and reactive approaches.
Java HttpClient (Java 11+)
import java.net.http.*;
import java.net.URI;
import com.fasterxml.jackson.databind.ObjectMapper;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://geo.kamero.ai/api/geo"))
.timeout(Duration.ofSeconds(5))
.GET()
.build();
var response = client.send(request,
HttpResponse.BodyHandlers.ofString());
var mapper = new ObjectMapper();
var geo = mapper.readValue(
response.body(), GeoResponse.class);
System.out.printf("%s, %s (%s)%n",
geo.city(), geo.country(), geo.timezone());Data Model
// Using Java record (Java 16+)
public record GeoResponse(
String ip,
String city,
String country,
String countryRegion,
String continent,
String latitude,
String longitude,
String timezone,
String postalCode,
String region
) {}Spring Boot with RestTemplate
@Service
public class GeoLocationService {
private final RestTemplate rest;
public GeoLocationService(RestTemplateBuilder builder) {
this.rest = builder
.setConnectTimeout(Duration.ofSeconds(3))
.setReadTimeout(Duration.ofSeconds(5))
.build();
}
public Optional<GeoResponse> getLocation() {
try {
var geo = rest.getForObject(
"https://geo.kamero.ai/api/geo",
GeoResponse.class);
return Optional.ofNullable(geo);
} catch (RestClientException e) {
log.warn("Geo lookup failed: {}", e.getMessage());
return Optional.empty();
}
}
}Reactive with WebClient
@Service
public class ReactiveGeoService {
private final WebClient webClient;
public ReactiveGeoService(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("https://geo.kamero.ai")
.build();
}
public Mono<GeoResponse> getLocation() {
return webClient.get()
.uri("/api/geo")
.retrieve()
.bodyToMono(GeoResponse.class)
.timeout(Duration.ofSeconds(5))
.onErrorResume(e -> {
log.warn("Geo lookup failed: {}",
e.getMessage());
return Mono.empty();
});
}
}Add Caching with Spring Cache
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
var cache = new ConcurrentMapCacheManager("geo");
return cache;
}
}
@Service
public class CachedGeoService {
private final GeoLocationService geoService;
@Cacheable(value = "geo", key = "'current'")
public Optional<GeoResponse> getLocation() {
return geoService.getLocation();
}
@CacheEvict(value = "geo", allEntries = true)
@Scheduled(fixedRate = 600_000) // 10 minutes
public void evictCache() {
log.debug("Geo cache evicted");
}
}Servlet Filter for Request Enrichment
@Component
@Order(1)
public class GeoLocationFilter extends OncePerRequestFilter {
private final GeoLocationService geoService;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws Exception {
geoService.getLocation().ifPresent(geo -> {
request.setAttribute("geoCountry",
geo.country());
request.setAttribute("geoCity",
geo.city());
request.setAttribute("geoTimezone",
geo.timezone());
});
chain.doFilter(request, response);
}
}Use in a Controller
@RestController
@RequestMapping("/api")
public class LocationController {
private final GeoLocationService geoService;
@GetMapping("/my-location")
public ResponseEntity<GeoResponse> getLocation() {
return geoService.getLocation()
.map(ResponseEntity::ok)
.orElse(ResponseEntity
.status(HttpStatus.SERVICE_UNAVAILABLE)
.build());
}
@GetMapping("/greeting")
public Map<String, String> greeting(
HttpServletRequest request) {
var country = (String) request
.getAttribute("geoCountry");
var greeting = switch (country) {
case "JP" -> "こんにちは";
case "ES", "MX" -> "¡Hola!";
case "FR" -> "Bonjour";
case "DE" -> "Hallo";
default -> "Hello";
};
return Map.of("greeting", greeting);
}
}Key Takeaways
- Java records make clean, immutable geo data models
- Use
RestTemplatefor blocking orWebClientfor reactive apps - Spring Cache reduces redundant API calls
- Servlet filters enrich every request with location data
- Always set timeouts and handle failures with
Optional
Try the API
Free IP geolocation with no API key — works with any Java HTTP client.
View Documentation →