Integration guide

Add currency exchange rates and conversion to C# and .NET 8

Use HttpClient, System.Text.Json, and IMemoryCache. Cache the latest response to its nextUpdateAt and keep credentials in server configuration.

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.cs

C#
using System.Globalization;using System.Net.Http.Headers;using System.Net.Http.Json;using Microsoft.Extensions.Caching.Memory; public record Latest(    string Base,    DateTimeOffset UpdatedAt,    DateTimeOffset NextUpdateAt,    Dictionary<string, decimal> Rates); public record Conversion(    string From,    string To,    decimal Amount,    decimal Rate,    decimal Result,    DateTimeOffset UpdatedAt); public sealed class RatesClient(    HttpClient http,    IMemoryCache cache,    IConfiguration config){    const string Api = "https://api.currencyexchangerate-api.com/api/v1";     async Task<T> Get<T>(string path)    {        using var req = new HttpRequestMessage(HttpMethod.Get, Api + path);        req.Headers.Authorization =            new AuthenticationHeaderValue("Bearer", config["CXR_API_KEY"]);        using var res = await http.SendAsync(req);        if (!res.IsSuccessStatusCode)        {            var retryAfter = res.Headers.RetryAfter?.Delta?.TotalSeconds;            throw new HttpRequestException(                "API status " + (int)res.StatusCode + "; Retry-After " + retryAfter);        }        return (await res.Content.ReadFromJsonAsync<T>())!;    }     public async Task<Latest> GetLatest()    {        if (cache.TryGetValue("latest", out Latest? saved))        {            return saved!;        }        var data = await Get<Latest>("/latest?base=USD&symbols=EUR,GBP");        cache.Set("latest", data, data.NextUpdateAt);        return data;    }     public Task<Conversion> Convert(string from, string to, decimal amount)    {        var query = "from=" + Uri.EscapeDataString(from)            + "&to=" + Uri.EscapeDataString(to)            + "&amount=" + amount.ToString(CultureInfo.InvariantCulture);        return Get<Conversion>("/convert?" + query);    }}

3. Use it in C# and .NET 8

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

Program.cs

C#
using System.Globalization; var builder = WebApplication.CreateBuilder(args);builder.Services.AddMemoryCache();builder.Services.AddHttpClient<RatesClient>();var app = builder.Build(); app.MapGet("/convert", async (RatesClient rates) =>{    var result = await rates.Convert("EUR", "GBP", 100);    var culture = CultureInfo.GetCultureInfo("en-GB");    return new { result, formatted = result.Result.ToString("C", culture) };}); app.Run();

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.cs

C#
try{    var result = await rates.Convert("EUR", "GBP", 100);}catch (HttpRequestException e){    // e.g. "API status 429; Retry-After 30"    Console.Error.WriteLine(e.Message);}

5. Format the amount for display

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

Format.cs

C#
using System.Globalization; var culture = CultureInfo.GetCultureInfo("de-DE");Console.WriteLine(85.249105m.ToString("C", culture));