Integration guide

Add currency exchange rates and conversion to Java 17

Use Java 17 HttpClient and Jackson with typed response records. Cache current rates until nextUpdateAt and send the key only from server code.

1. Get a free API key

Sign up at https://currencyexchangerate-api.com/auth/login, then create a key under API keys in the console. The free plan includes 3,000 requests per month with no credit card. Store the secret as CXR_API_KEY in server-side environment configuration; never expose it in browser code.

.env

shell
CXR_API_KEY=cxr_your_secret_key

2. Create a reusable client and cache rates

Use the standard HTTP client. Cache every /latest response until nextUpdateAt: rates change at most once by then, and reusing the response saves quota. The helper also exposes /convert.

RatesClient.java

Java
// Dependencies: jackson-databind and jackson-datatype-jsr310import com.fasterxml.jackson.databind.DeserializationFeature;import com.fasterxml.jackson.databind.ObjectMapper;import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.time.Instant;import java.util.Map; class RatesClient {    static final String API = "https://api.currencyexchangerate-api.com/api/v1";     record Latest(            String base,            String updatedAt,            Instant nextUpdateAt,            Map<String, Double> rates) {}     record Conversion(            String from,            String to,            double amount,            double rate,            double result,            String updatedAt) {}     final HttpClient http = HttpClient.newHttpClient();    final ObjectMapper json = new ObjectMapper()            .findAndRegisterModules()            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);    Latest cached;     <T> T get(String path, Class<T> type) throws Exception {        var req = HttpRequest.newBuilder(URI.create(API + path))                .header("Authorization", "Bearer " + System.getenv("CXR_API_KEY"))                .GET()                .build();        var res = http.send(req, HttpResponse.BodyHandlers.ofString());        if (res.statusCode() >= 400) {            String retryAfter = res.headers().firstValue("Retry-After").orElse("");            throw new IllegalStateException(                    "API status " + res.statusCode() + " Retry-After " + retryAfter);        }        return json.readValue(res.body(), type);    }     Latest latest() throws Exception {        if (cached == null || Instant.now().isAfter(cached.nextUpdateAt())) {            cached = get("/latest?base=USD&symbols=EUR,GBP", Latest.class);        }        return cached;    }     Conversion convert(String from, String to, double amount) throws Exception {        String path = "/convert?from=" + from + "&to=" + to + "&amount=" + amount;        return get(path, Conversion.class);    }}

3. Use it in Java 17

Call the helper from a server route, controller, view, command, or script. Format converted values for the user’s locale.

Main.java

Java
import java.text.NumberFormat;import java.util.Currency;import java.util.Locale; public class Main {    public static void main(String[] args) throws Exception {        var result = new RatesClient().convert("EUR", "GBP", 100);        var formatter = NumberFormat.getCurrencyInstance(Locale.UK);        formatter.setCurrency(Currency.getInstance(result.to()));        System.out.println(formatter.format(result.result()));    }}

4. Handle API errors

A 401 means the key is invalid. A 429 can mean a rate limit or quota limit; read Retry-After in seconds before retrying. Other errors use application/problem+json.

Errors.java

Java
try {    new RatesClient().convert("EUR", "GBP", 100);} catch (IllegalStateException e) {    // e.g. "API status 429 Retry-After 30"    System.err.println(e.getMessage());}

5. Format the amount for display

Use locale-aware currency formatting instead of adding a symbol manually.

Format.java

Java
import java.text.NumberFormat;import java.util.Currency;import java.util.Locale; var formatter = NumberFormat.getCurrencyInstance(Locale.GERMANY);formatter.setCurrency(Currency.getInstance("EUR"));System.out.println(formatter.format(85.249105));