Si vous construisez un backend de paiement, un moteur de facturation SaaS ou un service de tarification d'entreprise sur la stack Microsoft, intégrer une API de taux de change en C# / .NET est l'une des tâches les plus rentables que vous puissiez livrer cette semaine. Le .NET moderne vous donne tout ce qu'il faut prêt à l'emploi — HttpClient, IHttpClientFactory, System.Text.Json, IMemoryCache et l'injection de dépendances de premier ordre — et pourtant la plupart des tutoriels affichent encore un unique snippet WebClient.DownloadString qui ne passerait aucune revue de code en production. Ce guide vous emmène de votre première requête jusqu'à un client typé, mis en cache et résilient que vous pouvez déposer dans ASP.NET Core, une application Blazor, un outil de bureau WPF ou une Azure Function.
À la fin de ce tutoriel, vous aurez un client Finexly réutilisable pour la documentation de l'API Finexly, un petit convertisseur en console et une API minimale ASP.NET Core qui sert des taux de change en temps réel au reste de votre stack — le tout écrit dans un .NET 8 / .NET 9 idiomatique avec les patterns recommandés par Microsoft.
Pourquoi C# / .NET est un excellent choix pour les intégrations de devises
La conversion de devises en production est principalement un problème d'I/O : récupérer les taux, les cacher, multiplier quelques décimales, renvoyer du JSON. Le runtime .NET a été optimisé pour exactement cette charge depuis dix ans :
IHttpClientFactoryrègle l'épuisement des sockets. Il met en pool les instances deHttpMessageHandlerpour que vous puissiez injecterHttpClientpartout sans fuites de sockets — la principale source de pannes intermittentes d'API de change que nous voyons dans du code .NET hérité.System.Text.Jsonest rapide et économe en allocations. Il désérialise une réponse typique de 170 devises bien en dessous d'une milliseconde, sans dépendance tierce.decimalest intégré au langage. Vous n'avez jamais besoin d'unBigDecimaltiers pour manipuler de l'argent correctement — et ça compte quand vous additionnez des milliers de lignes de factures converties.- Polly est la bibliothèque de résilience de fait. Retries, circuit breakers et timeouts sont à un
AddPolicyHandlerprès. - APIs minimales et AOT. Démarrer un micro-service de taux en 30 lignes est aujourd'hui réaliste, et il démarre à froid en millisecondes.
Si vous évaluez d'autres stacks, nous avons des guides parallèles pour Node.js, Python, Go et PHP. Cet article cible spécifiquement C# et suppose que vous êtes à l'aise avec async/await, le generic hosting et l'injection de dépendances.
Prérequis et mise en place du projet
Vous avez besoin de .NET 8 ou .NET 9. Vérifiez votre SDK :
dotnet --versionCréez une solution avec une bibliothèque de classes pour le client et une application console pour l'exercer :
mkdir Finexly.Sample && cd Finexly.Sample
dotnet new sln -n Finexly.Sample
dotnet new classlib -n Finexly.Client
dotnet new console -n Finexly.ConsoleApp
dotnet sln add Finexly.Client/Finexly.Client.csproj
dotnet sln add Finexly.ConsoleApp/Finexly.ConsoleApp.csproj
dotnet add Finexly.ConsoleApp/Finexly.ConsoleApp.csproj reference Finexly.Client/Finexly.Client.csprojIl vous faut aussi une API key Finexly. Inscrivez-vous gratuitement — sans carte bancaire, et l'offre gratuite inclut 1 000 requêtes par mois, largement de quoi développer et faire tourner du CI.
Ne codez jamais la clé en dur. Stockez-la dans une variable d'environnement :
# macOS / Linux
export FINEXLY_API_KEY="your_api_key_here"# Windows PowerShell
$env:FINEXLY_API_KEY = "your_api_key_here"En production, stockez-la dans Azure Key Vault, AWS Secrets Manager ou dotnet user-secrets pour le développement local.
Votre première requête avec HttpClient
Commençons par l'exemple fonctionnel le plus simple. Ouvrez Finexly.ConsoleApp/Program.cs et écrivez :
using System.Net.Http;
using System.Net.Http.Json;
var apiKey = Environment.GetEnvironmentVariable("FINEXLY_API_KEY")
?? throw new InvalidOperationException("FINEXLY_API_KEY not set");
using var http = new HttpClient
{
BaseAddress = new Uri("https://api.finexly.com/v1/")
};
var url = $"latest?base=USD&apikey={apiKey}";
var payload = await http.GetFromJsonAsync<Dictionary<string, object>>(url);
Console.WriteLine(payload?["rates"]);Exécutez-le :
dotnet run --project Finexly.ConsoleAppVous verrez un dictionnaire des codes de devise et de leurs taux par rapport au USD. Ça marche, mais ça a toutes les odeurs attendues d'un exemple de 10 lignes : valeurs object non typées, pas de cache, pas de retries, un HttpClient instancié manuellement et l'API key dans l'URL. Corrigeons tout ça.
Définir des modèles de réponse fortement typés
System.Text.Json désérialise proprement vers des record C#. Dans le projet Finexly.Client, créez Models/LatestRatesResponse.cs :
using System.Text.Json.Serialization;
namespace Finexly.Client.Models;
public sealed record LatestRatesResponse(
[property: JsonPropertyName("base")] string Base,
[property: JsonPropertyName("date")] DateOnly Date,
[property: JsonPropertyName("rates")] IReadOnlyDictionary<string, decimal> Rates
);Quelques détails à noter :
decimalplutôt quedouble. L'erreur de virgule flottante est inacceptable pour de l'argent.decimalvous donne 28–29 chiffres significatifs et une arithmétique exacte en base 10.IReadOnlyDictionary. La réponse est en lecture seule — exprimez-le dans le type.DateOnly. Le .NET moderne livre un vrai type date. Utilisez-le à la place deDateTimequand il n'y a pas d'heure.
Maintenant on peut désérialiser sans gymnastique avec dynamic ou Dictionary<string, object>.
Construire un HttpClient typé avec IHttpClientFactory
Le pattern recommandé en .NET est un client typé enregistré via IHttpClientFactory. Il vous offre du pooling de connexion, des handlers configurables et une DI simple. Créez FinexlyClient.cs dans le projet Finexly.Client :
using System.Net.Http.Json;
using Finexly.Client.Models;
namespace Finexly.Client;
public sealed class FinexlyClient
{
private readonly HttpClient _http;
private readonly string _apiKey;
public FinexlyClient(HttpClient http, FinexlyOptions options)
{
_http = http;
_apiKey = options.ApiKey;
_http.BaseAddress = new Uri("https://api.finexly.com/v1/");
_http.DefaultRequestHeaders.Add("X-Api-Key", _apiKey);
_http.Timeout = TimeSpan.FromSeconds(5);
}
public async Task<LatestRatesResponse> GetLatestRatesAsync(
string baseCurrency = "USD",
IEnumerable<string>? symbols = null,
CancellationToken cancellationToken = default)
{
var query = $"latest?base={baseCurrency}";
if (symbols is not null)
{
query += $"&symbols={string.Join(",", symbols)}";
}
var response = await _http.GetFromJsonAsync<LatestRatesResponse>(
query, cancellationToken);
return response
?? throw new InvalidOperationException("Empty response from Finexly");
}
public async Task<decimal> ConvertAsync(
string from, string to, decimal amount,
CancellationToken cancellationToken = default)
{
var rates = await GetLatestRatesAsync(from, new[] { to }, cancellationToken);
if (!rates.Rates.TryGetValue(to, out var rate))
{
throw new ArgumentException($"Unknown currency: {to}");
}
return amount * rate;
}
}
public sealed class FinexlyOptions
{
public required string ApiKey { get; init; }
}Quelques points à souligner :
- L'API key passe dans un header, pas dans l'URL — plus sûr pour les logs, les proxies et le tracing.
- Un
Timeoutde 5 secondes empêche une API de change figée de transformer un checkout en checkout lent. - Le client prend un
CancellationTokensur chaque méthode — c'est non négociable en ASP.NET moderne. - Il utilise
GetFromJsonAsync<T>, qui combine le GET, la vérification de statut et la désérialisation en un seul appel peu coûteux en allocations.
Enregistrer le client dans l'injection de dépendances
Dans votre Program.cs (Console, ASP.NET Core, Worker — même API partout), branchez-le :
using Finexly.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton(new FinexlyOptions
{
ApiKey = Environment.GetEnvironmentVariable("FINEXLY_API_KEY")!
});
builder.Services.AddHttpClient<FinexlyClient>();
using var host = builder.Build();
var finexly = host.Services.GetRequiredService<FinexlyClient>();
var eur = await finexly.ConvertAsync("USD", "EUR", 100m);
Console.WriteLine($"100 USD = {eur:0.00} EUR");AddHttpClient<FinexlyClient>() enregistre FinexlyClient en tant que service transient et lui donne un HttpClient pooled venant de la factory. Vous pouvez maintenant injecter FinexlyClient dans n'importe quel controller, endpoint d'API minimale ou service en arrière-plan.
Ajouter retries et circuit breaker avec Polly
Les APIs externes laissent parfois tomber une requête, font du throttling ou ont un hoquet réseau. Polly est la bibliothèque de résilience standard en .NET et s'intègre à IHttpClientFactory en deux lignes.
Installez le package :
dotnet add Finexly.ConsoleApp package Microsoft.Extensions.Http.PollyConfigurez un retry avec backoff et un circuit breaker :
using Polly;
using Polly.Extensions.Http;
builder.Services.AddHttpClient<FinexlyClient>()
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(r => (int)r.StatusCode == 429)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt =>
TimeSpan.FromMilliseconds(200 * Math.Pow(2, attempt))))
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30)));Cet unique enregistrement réessaye sur 5xx, 408 et 429 en backoff exponentiel (200 ms, 400 ms, 800 ms), et déclenche un circuit breaker si l'API est complètement KO — votre page de checkout dégrade gracieusement au lieu de garder des connexions ouvertes pendant des minutes.
Mettre en cache les réponses avec IMemoryCache
Les taux de change en temps réel changent rarement plus d'une fois par minute, donc un petit cache en processus réduit typiquement les appels upstream de 95 % ou plus. .NET livre IMemoryCache en standard.
dotnet add Finexly.Client package Microsoft.Extensions.Caching.MemoryCréez CachedFinexlyClient.cs :
using Finexly.Client.Models;
using Microsoft.Extensions.Caching.Memory;
namespace Finexly.Client;
public sealed class CachedFinexlyClient
{
private readonly FinexlyClient _inner;
private readonly IMemoryCache _cache;
private static readonly TimeSpan DefaultTtl = TimeSpan.FromSeconds(60);
public CachedFinexlyClient(FinexlyClient inner, IMemoryCache cache)
{
_inner = inner;
_cache = cache;
}
public Task<LatestRatesResponse> GetLatestRatesAsync(
string baseCurrency,
CancellationToken cancellationToken = default)
{
var key = $"finexly:latest:{baseCurrency}";
return _cache.GetOrCreateAsync(key, entry =>
{
entry.AbsoluteExpirationRelativeToNow = DefaultTtl;
return _inner.GetLatestRatesAsync(baseCurrency,
cancellationToken: cancellationToken);
})!;
}
}Enregistrez-le aux côtés de IMemoryCache :
builder.Services.AddMemoryCache();
builder.Services.AddSingleton<CachedFinexlyClient>();Un TTL de 60 secondes est une excellente valeur par défaut pour les prix de checkout, les aperçus de facturation et les tableaux de bord admin. Pour les données historiques, montez le TTL à des heures ou des jours — les taux historiques ne changent pas.
Un convertisseur de devises en console fonctionnel
En mettant tout ensemble, voici une petite CLI qui reproduit currencyconverter --amount 100 --from USD --to JPY :
using Finexly.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddSingleton(new FinexlyOptions
{
ApiKey = Environment.GetEnvironmentVariable("FINEXLY_API_KEY")!
});
builder.Services.AddHttpClient<FinexlyClient>();
builder.Services.AddSingleton<CachedFinexlyClient>();
using var host = builder.Build();
decimal amount = 1m;
string from = "USD", to = "EUR";
for (int i = 0; i < args.Length - 1; i++)
{
switch (args[i])
{
case "--amount": amount = decimal.Parse(args[i + 1]); break;
case "--from": from = args[i + 1].ToUpperInvariant(); break;
case "--to": to = args[i + 1].ToUpperInvariant(); break;
}
}
var finexly = host.Services.GetRequiredService<CachedFinexlyClient>();
var rates = await finexly.GetLatestRatesAsync(from);
if (!rates.Rates.TryGetValue(to, out var rate))
{
Console.Error.WriteLine($"Unknown currency: {to}");
return 1;
}
Console.WriteLine($"{amount:0.00} {from} = {amount * rate:0.00} {to}");
return 0;Construisez un binaire mono-fichier que vous pouvez déposer sur n'importe quel serveur :
dotnet publish Finexly.ConsoleApp -c Release -r linux-x64 \
--self-contained -p:PublishSingleFile=trueExposer les taux avec une API minimale ASP.NET Core
Pour la plupart des équipes, la bonne architecture est un micro-service interne de taux qui enveloppe l'API upstream, ajoute du cache et offre à tous les autres services un endpoint rapide, gratuit, dans le même VPC. Les APIs minimales d'ASP.NET Core font de ça un fichier de 30 lignes :
using Finexly.Client;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddSingleton(new FinexlyOptions
{
ApiKey = builder.Configuration["Finexly:ApiKey"]!
});
builder.Services.AddHttpClient<FinexlyClient>();
builder.Services.AddSingleton<CachedFinexlyClient>();
var app = builder.Build();
app.MapGet("/rates", async (
string? @base,
CachedFinexlyClient finexly,
CancellationToken ct) =>
{
var rates = await finexly.GetLatestRatesAsync(@base ?? "USD", ct);
return Results.Ok(rates);
});
app.MapGet("/convert", async (
string from, string to, decimal amount,
CachedFinexlyClient finexly,
CancellationToken ct) =>
{
var rates = await finexly.GetLatestRatesAsync(from, ct);
return rates.Rates.TryGetValue(to, out var rate)
? Results.Ok(new { from, to, amount, converted = amount * rate })
: Results.NotFound(new { error = $"Unknown currency {to}" });
});
app.Run();Déployez ça derrière votre API gateway habituelle et n'importe quel service de votre flotte pourra appeler GET /convert?from=USD&to=EUR&amount=99.95 avec une latence sous les 10 millisecondes en cache hit. Pour une expérience navigateur, les mêmes données sont disponibles dans notre convertisseur de devises hébergé.
Gérer correctement l'argent en C#
Quelques patterns défensifs qui se rentabilisent dès le premier audit de grand livre :
- Utilisez toujours
decimal, jamaisdoublenifloat.0.1 + 0.2n'est célèbrement pas égal à0.3en virgule flottante — et cette unique erreur d'arrondi peut vous coûter un litige de chargeback. - Arrondissez à la présentation, pas au calcul. Gardez la précision complète à travers chaque multiplication et n'utilisez
Math.Round(value, 2, MidpointRounding.ToEven)qu'au moment d'afficher ou de persister. - Fixez le mode d'arrondi. L'arrondi banquier (
ToEven) est le défaut de l'industrie financière dans IEEE 754 et ISO 80000-1. - Traitez les devises comme des codes opaques. Utilisez des chaînes ISO 4217 — ne supposez jamais qu'une devise a deux décimales. JPY en a zéro, BHD en a trois. Notre guide ISO 4217 couvre les cas limites.
Bonnes pratiques pour la production
Quelques patterns supplémentaires qui séparent une intégration de loisir d'un système exploitable pendant des années :
- Configurez des timeouts HTTP serrés. Le
HttpClient.Timeoutpar défaut de 100 secondes bloquera votre thread pool sous charge. Cinq secondes suffisent largement pour une API de change. - Enveloppez chaque appel externe dans un
CancellationToken. ASP.NET Core vous en passe un gratuitement ; respectez-le pour que les requêtes abandonnées ne continuent pas à parler à l'upstream. - Logguez les codes de statut, pas les corps. Les réponses de l'API de change sont trop grosses pour être loguées utilement et peuvent contenir des données de taux que vous ne voulez pas dans Splunk.
- Surveillez votre quota. Comparez les plans tarifaires et configurez des alertes CloudWatch / Application Insights à 80 % de votre plafond mensuel.
- Pinglez uniquement les devises que vous utilisez. Envoyer
symbols=EUR,GBP,JPYréduit la réponse de ~10 Ko à moins de 200 octets. - Cachez agressivement. Les checkouts sensibles à la latence peuvent vivre avec un cache de 30 secondes ; les dashboards admin peuvent tenir avec 5 minutes.
Si vous évaluez des fournisseurs, notre comparaison APIs de change gratuites vs payantes parcourt précision, uptime et prix chez les principales options.
Questions fréquentes
Quelle API de change fonctionne le mieux avec C# et .NET ?
N'importe quelle API REST avec du JSON propre et un uptime stable s'intégrera bien via IHttpClientFactory. Finexly est spécifiquement conçue pour être agnostique au langage — les mêmes endpoints sont consommés par des clients Node, Python, Go, Java et .NET sans verrouillage par SDK. Voyez notre vue d'ensemble de l'API gratuite pour le contexte.
Newtonsoft.Json ou System.Text.Json ?
Pour du nouveau code en .NET 6+, utilisez System.Text.Json. Il est 2–3× plus rapide, alloue moins et fait partie de la BCL, donc aucun paquet supplémentaire à maintenir. Newtonsoft.Json reste excellent et mérite d'être gardé dans le code legacy, mais les nouvelles intégrations de change en C# devraient par défaut utiliser System.Text.Json.
Ai-je besoin d'un SDK NuGet ou HttpClient suffit ? Un client typé écrit à la main (environ 50 lignes de C#) est presque toujours la bonne réponse. Il vous donne un contrôle total sur le cache, les retries, la télémétrie et la sérialisation, et évite le risque de désalignement de versions qui accompagne tout SDK tiers. L'exemple de ce tutoriel est prêt pour la production.
À quelle fréquence rafraîchir les taux dans une application .NET ? Pour la plupart des cas — checkouts e-commerce, facturation SaaS, facturation client — un cache de 30 à 60 secondes est sûr. Les systèmes de trading ont évidemment besoin de temps réel, mais pour tout le reste, une fraîcheur à la minute est invisible pour les utilisateurs et réduit drastiquement la dépense en API.
Puis-je faire tourner ça dans Azure Functions ou AWS Lambda ?
Oui. IHttpClientFactory fonctionne pareil dans Azure Functions en processus isolé et dans les handlers Lambda avec Microsoft.Extensions.Hosting. Pour les charges sensibles au cold start, publiez avec AOT (PublishAot=true) et votre fonction démarrera en moins de 100 ms.
decimal est-il lent par rapport à double ?
decimal est environ 10 à 20× plus lent que double par opération arithmétique, mais une conversion de devises ne fait que quelques multiplications. Vous ne mesurerez jamais la différence dans une charge réelle — et le gain de justesse est non négociable pour de l'argent.
Conclusion
Vous avez maintenant un blueprint complet et de niveau production pour intégrer une API de taux de change en C# / .NET : modèles fortement typés, client adossé à IHttpClientFactory, retries et circuit breaker via Polly, cache en processus, convertisseur console et API minimale ASP.NET Core. Chaque pattern ici sort directement des recommandations officielles de Microsoft et a été éprouvé dans du code fintech en production.
Prêt à livrer des taux en temps réel dans votre application .NET ? Obtenez votre clé API Finexly gratuite — sans carte bancaire. Démarrez avec 1 000 requêtes gratuites par mois et passez à l'échelle quand votre produit grandit. Si vous voulez d'abord voir comment Finexly se compare aux autres fournisseurs, lisez notre comparaison des APIs de change.
Explore More
Vlado Grigirov
Senior Currency Markets Analyst & Financial Strategist
Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...
View full profile →