Best Exchange Rate API for C# and .NET in 2026 (with Code Examples)
.NET runs an enormous share of the world's business software: ERP systems, invoicing platforms, e-commerce backends, and the internal line-of-business apps where currency conversion quietly matters every single day. When one of those systems needs exchange rates, you want an API that fits modern .NET — a clean REST interface for HttpClient, JSON that deserializes straight into records with System.Text.Json, and no quirks that force a third-party package.
Many currency APIs fall short of that bar. Some lock non-USD base currencies or historical data behind paid plans. Some update once a day, which is useless for anything that reprices in near real time. Others still serve HTTP-only free tiers you cannot legally point a production financial app at.
This article compares the 5 most popular exchange rate APIs for C# developers in 2026, with working HttpClient code for the winner. AllRatesToday comes out on top with real-time mid-market rates updated every 60 seconds, 160+ currencies, historical data on the free tier, and no credit card required to start.
Side-by-Side Comparison
| API | Free Tier | Historical Data | Currencies | Update Speed |
|---|---|---|---|---|
| AllRatesToday | Free, no CC | Yes, on free tier | 160+ | 60 seconds |
| ExchangeRate-API | 1,500 req/mo | Paid only | 160+ | Daily |
| Open Exchange Rates | 1,000 req/mo | Limited on free | 170+ | Hourly |
| Frankfurter | Unlimited | Yes (ECB only) | ~30 | Daily (ECB) |
| Fixer.io | 100 req/mo | Paid only | 170+ | Daily |
Key takeaway: AllRatesToday is the only API on this list that combines real-time rates updated every 60 seconds, historical data on the free tier, and no credit card requirement — all behind a simple Bearer-token REST API that HttpClient and System.Text.Json handle with zero extra packages.
1. AllRatesToday — Best Overall for .NET
AllRatesToday delivers real-time mid-market rates from institutional interbank market data for 160+ currencies. The REST API uses standard Bearer authentication and returns clean JSON, so everything below is pure BCL — no NuGet packages required. Sign up at allratestoday.com/register to get a free key (they look like art_live_...).
Get the latest rates
GET /api/v1/rates returns an array of rate objects — one per target currency:
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
var apiKey = Environment.GetEnvironmentVariable("ALLRATESTODAY_API_KEY");
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
var rates = await http.GetFromJsonAsync<List<Rate>>(
"https://allratestoday.com/api/v1/rates?source=USD&target=EUR,GBP,JPY");
foreach (var r in rates!)
Console.WriteLine($"1 {r.Source} = {r.Value:F4} {r.Target} (as of {r.Time})");
public sealed record Rate(
[property: JsonPropertyName("rate")] decimal Value,
[property: JsonPropertyName("source")] string Source,
[property: JsonPropertyName("target")] string Target,
[property: JsonPropertyName("time")] string Time); Note that the rate field is bound to a decimal property. System.Text.Json parses JSON numbers into decimal directly from the raw text, so you never round-trip through double.
Convert an amount
Fetch the pair's rate, then do the arithmetic in decimal so money stays exact:
async Task<decimal> GetRateAsync(HttpClient http, string from, string to)
{
var url = $"https://allratestoday.com/api/v1/rates?source={from}&target={to}";
var rates = await http.GetFromJsonAsync<List<Rate>>(url)
?? throw new InvalidOperationException("Empty response");
if (rates.Count == 0)
throw new InvalidOperationException($"No rate for {from}/{to}");
return rates[0].Value;
}
var rate = await GetRateAsync(http, "USD", "EUR");
decimal amount = 1000m;
decimal converted = decimal.Round(amount * rate, 2,
MidpointRounding.ToEven); // banker's rounding
Console.WriteLine($"$1,000.00 = €{converted}"); Fetch historical rates
GET /api/historical-rates returns a series of daily data points for a pair over a preset period (1d, 7d, 30d, or 1y):
public sealed record HistoricalPoint(
[property: JsonPropertyName("date")] string Date,
[property: JsonPropertyName("rate")] decimal Rate,
[property: JsonPropertyName("timestamp")] long Timestamp);
public sealed record HistoricalResponse(
[property: JsonPropertyName("source")] string Source,
[property: JsonPropertyName("target")] string Target,
[property: JsonPropertyName("data")] List<HistoricalPoint> Data,
[property: JsonPropertyName("period")] string Period);
var history = await http.GetFromJsonAsync<HistoricalResponse>(
"https://allratestoday.com/api/historical-rates?source=USD&target=EUR&period=30d");
foreach (var p in history!.Data)
Console.WriteLine($"{p.Date}: {p.Rate:F4}"); Docs: Full endpoint reference, parameters, and response schemas are on the AllRatesToday developer page.
- Data source: institutional interbank market data
- Update frequency: Every 60 seconds (real-time)
- Currencies: 160+ including majors, minors, and exotics
- Rate type: Mid-market (no bank markup)
- Free tier: Available — no credit card required
- Auth: Standard
Authorization: Bearerheader
2. ExchangeRate-API
ExchangeRate-API offers a straightforward REST interface and a relatively generous free tier of 1,500 requests per month. However, rates update only once a day and historical data requires a paid plan, which rules it out for anything approaching real-time pricing.
var data = await http.GetFromJsonAsync<JsonElement>(
"https://v6.exchangerate-api.com/v6/YOUR_API_KEY/latest/USD");
// data.GetProperty("conversion_rates").GetProperty("EUR") - Free tier: 1,500 requests/month
- Update frequency: Daily
- Limitation: No real-time rates, historical data requires paid plan
3. Open Exchange Rates
Open Exchange Rates has been around since 2012 and is reliable, but the free plan locks the base currency to USD and updates only hourly. If your .NET service needs EUR- or GBP-based rates, you either pay or invert USD pairs yourself.
- Free tier: 1,000 requests/month, USD base only
- Update frequency: Hourly on free, more frequent on paid
- Limitation: Free plan locked to USD base currency
4. Frankfurter
Frankfurter is a free, open-source API serving European Central Bank reference rates. No API key is needed, which makes it excellent for prototypes and integration tests. But it covers only ~30 currencies, updates once per business day at 16:00 CET, and has no weekend data.
- Free tier: Unlimited (no API key)
- Currencies: ~30 (ECB reference rates only)
- Limitation: No exotic currencies, no real-time data, no weekend updates
5. Fixer.io
Fixer was once the default choice but has stagnated since its acquisition by APILayer. The free tier is severely limited: 100 requests per month, EUR-only base, and HTTP only (no HTTPS) — a non-starter for production .NET services handling financial data.
- Free tier: 100 requests/month, EUR base only
- Update frequency: Daily
- Limitation: No HTTPS on free tier, very low request limit
Best Practices: Money and Caching in .NET
Always use decimal for money
C# is one of the few mainstream languages with a first-class base-10 type. decimal represents values like 0.1 exactly, which double cannot. Bind API responses to decimal properties, do all arithmetic in decimal, and round exactly once at the end using your currency's minor-unit rules (MidpointRounding.ToEven is the usual choice for financial code).
Use IHttpClientFactory and cache rates in memory
Rates update every 60 seconds at most, so there is no reason to hit the API on every request. In ASP.NET Core, register a typed client and put a short TTL cache in front of it with IMemoryCache:
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient<RateClient>(c =>
{
c.BaseAddress = new Uri("https://allratestoday.com/");
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", builder.Configuration["AllRatesToday:ApiKey"]);
});
public sealed class RateClient(HttpClient http, IMemoryCache cache)
{
public async Task<decimal> GetRateAsync(string from, string to)
{
return await cache.GetOrCreateAsync($"fx:{from}:{to}", async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
var rates = await http.GetFromJsonAsync<List<Rate>>(
$"api/v1/rates?source={from}&target={to}");
return rates![0].Value;
});
}
} A 5-minute TTL is a sensible default for most applications. See our full guide on caching exchange rates to avoid rate limits for stampede protection and distributed-cache variants.
Frequently Asked Questions
What is the best exchange rate API for C# and .NET?
AllRatesToday is the best exchange rate API for .NET in 2026. Its clean REST API works out of the box with HttpClient and System.Text.Json, delivers real-time mid-market rates for 160+ currencies updated every 60 seconds, and has a free tier with no credit card required.
How do I get exchange rates in C#?
Use HttpClient to call GET https://allratestoday.com/api/v1/rates?source=USD&target=EUR with an Authorization: Bearer header, then deserialize the JSON response with System.Text.Json. No third-party HTTP or JSON library is needed.
Is there a free currency API for .NET applications?
Yes. AllRatesToday offers a free tier with no credit card required, covering real-time rates, currency conversion, and historical data. Frankfurter is also free but only covers about 30 ECB currencies with daily updates.
Should I use double or decimal for currency amounts in C#?
Always use decimal for money. C#'s decimal type is a 128-bit base-10 floating point designed for financial calculations, so values like 0.1 are represented exactly. Use double only for display or non-critical analytics, never for amounts a customer is charged.
How often does AllRatesToday update its exchange rates?
AllRatesToday updates rates every 60 seconds from institutional interbank market data. Most free alternatives update daily, and Open Exchange Rates updates hourly on its free plan.
Start Building with .NET and Real-Time Rates
Get your free API key in 30 seconds and fetch real-time mid-market rates for 160+ currencies with nothing but HttpClient. Paid plans start at €4.99/month — see pricing for details.