Microsoft スタックの上に決済バックエンド、SaaS の課金エンジン、企業向けの価格サービスを構築しているなら、今週リリースできる最もインパクトの大きいタスクの一つが C# / .NET の為替レート API 統合です。モダンな .NET には必要なものがすべて標準で揃っています — HttpClient、IHttpClientFactory、System.Text.Json、IMemoryCache、そして一級市民の依存性注入 — それなのに大半のチュートリアルはいまだに、本番のコードレビューを絶対通らない WebClient.DownloadString のスニペットしか示しません。本ガイドは、最初のリクエストから、ASP.NET Core、Blazor アプリ、WPF デスクトップツール、Azure Function に投入できる型付き・キャッシュ付き・回復力のあるクライアントまで一気通貫で扱います。
このチュートリアルを終える頃には、Finexly API ドキュメントを叩く再利用可能な Finexly クライアント、小さなコンソール換算ツール、スタック全体にリアルタイムの為替レートを配信する ASP.NET Core Minimal API ができあがります — すべて Microsoft 自身が推奨するパターンに沿った、慣用的な .NET 8 / .NET 9 のコードです。
なぜ C# / .NET は為替統合に強い選択肢なのか
本番環境での為替変換は基本的に I/O 問題です — レートを取得し、キャッシュし、小数を掛け、JSON を返す。.NET ランタイムはこの 10 年、まさにこのワークロードに最適化されてきました。
IHttpClientFactoryはソケット枯渇を解決します。HttpMessageHandlerをプールするので、どこでもHttpClientを注入してソケットを漏らさずに済みます — レガシー .NET コードで見る通信断のもっとも大きな要因です。System.Text.Jsonは速く、アロケーションも少ない。 170 通貨の典型的なレスポンスをミリ秒未満でデシリアライズし、サードパーティ依存は不要です。decimalは言語組み込み。 通貨を正しく扱うのにサードパーティのBigDecimalは不要 — 換算された請求行を数千件合算するときにこの差は効きます。- Polly はデファクトの回復力ライブラリ。 リトライ、サーキットブレーカー、タイムアウトが
AddPolicyHandler1 行で書けます。 - Minimal API と AOT。 30 行で小さなレート用マイクロサービスを立ち上げるのが現実的で、コールドスタートはミリ秒です。
他のスタックを検討中なら Node.js、Python、Go、PHP の姉妹ガイドがあります。本記事は C# 専用で、async/await、Generic Hosting、依存性注入を前提とします。
前提条件とプロジェクト準備
.NET 8 または .NET 9 が必要です。SDK を確認:
dotnet --versionクライアントのクラスライブラリと、それを叩くコンソールアプリを含むソリューションを作成:
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.csprojFinexly の API キーも必要です。無料で登録 — クレジットカード不要で、無料プランは月 1,000 リクエスト付き、開発と CI には十分です。
キーはコードに直書きしないでください。環境変数に格納します:
# macOS / Linux
export FINEXLY_API_KEY="your_api_key_here"# Windows PowerShell
$env:FINEXLY_API_KEY = "your_api_key_here"本番では Azure Key Vault、AWS Secrets Manager、ローカル開発なら dotnet user-secrets を使います。
HttpClient での最初のリクエスト
最もシンプルな動くサンプルから始めましょう。Finexly.ConsoleApp/Program.cs を開き:
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"]);実行:
dotnet run --project Finexly.ConsoleApp通貨コードと USD に対するレートの辞書が表示されます。動きますが、10 行サンプル特有のにおいが全部あります — 型のない object、キャッシュなし、リトライなし、手動で HttpClient をインスタンス化、API キーが URL に。これを全部直していきます。
強く型付けされたレスポンスモデルを定義する
System.Text.Json は C# の record にきれいにデシリアライズします。Finexly.Client プロジェクトに 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
);押さえておく点:
doubleではなくdecimalを使う。 金額に対する浮動小数点誤差は許容できません。decimalは 28–29 桁の有効数字と、正確な 10 進演算を提供します。IReadOnlyDictionary。 レスポンスは読み取り専用 — 型で表現します。DateOnly。 モダンな .NET にはきちんとした日付型があります。時刻が不要ならDateTimeの代わりに使いましょう。
これで dynamic や Dictionary<string, object> の小細工なしにデシリアライズできます。
IHttpClientFactory で型付き HttpClient を組み立てる
.NET 推奨のパターンは、IHttpClientFactory 経由で登録する Typed Client です。接続プーリング、設定可能なハンドラ、容易な DI を提供します。Finexly.Client プロジェクトに FinexlyClient.cs:
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; }
}ポイント:
- API キーは URL ではなくヘッダーに — ログ・プロキシ・トレーシングで安全。
- 5 秒の
Timeoutで、詰まった為替 API がチェックアウト遅延を招くのを防ぎます。 - すべてのメソッドが
CancellationTokenを受け取ります — モダン ASP.NET では必須。 GetFromJsonAsync<T>を使い、GET・ステータスチェック・デシリアライズを 1 回の少アロケーション呼び出しにまとめます。
依存性注入にクライアントを登録する
Program.cs(Console、ASP.NET Core、Worker — どこでも同じ API)で結線:
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>() は FinexlyClient を transient で登録し、ファクトリからプール済み HttpClient を提供します。これで FinexlyClient をコントローラ、Minimal API エンドポイント、バックグラウンドサービスに自由に注入できます。
Polly でリトライとサーキットブレーカーを追加する
外部 API はときどきリクエストを落とし、スロットルし、ネットワーク層でしゃっくりします。Polly は .NET の標準的な回復力ライブラリで、IHttpClientFactory と 2 行で統合します。
パッケージをインストール:
dotnet add Finexly.ConsoleApp package Microsoft.Extensions.Http.Pollyバックオフ付きリトライとサーキットブレーカーを設定:
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)));この 1 回の登録で 5xx・408・429 を 200ms、400ms、800ms の指数バックオフでリトライし、API が完全に落ちていればサーキットブレーカーが作動します — チェックアウトページは何分も接続を握り続ける代わりに、優雅にデグレードします。
IMemoryCache でレスポンスをキャッシュする
ライブ為替レートは 1 分に 1 回以上変わることがめったになく、小さなインプロセスキャッシュで上流呼び出しを 95% 以上削減できるのが普通です。.NET には IMemoryCache が標準で入っています。
dotnet add Finexly.Client package Microsoft.Extensions.Caching.MemoryCachedFinexlyClient.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);
})!;
}
}IMemoryCache と一緒に登録:
builder.Services.AddMemoryCache();
builder.Services.AddSingleton<CachedFinexlyClient>();60 秒の TTL は、チェックアウト価格、請求プレビュー、管理ダッシュボードに最適なデフォルトです。ヒストリカルデータは数時間〜数日まで延ばせます — ヒストリカルは変わりません。
動くコンソール為替換算ツール
すべて組み合わせると currencyconverter --amount 100 --from USD --to JPY のような小さな CLI ができます:
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;どんなサーバーにも置ける単一ファイルバイナリをビルド:
dotnet publish Finexly.ConsoleApp -c Release -r linux-x64 \
--self-contained -p:PublishSingleFile=trueASP.NET Core Minimal API でレートを公開する
多くのチームにとって正しいアーキテクチャは、上流 API をラップしキャッシュを足し、他のすべてのサービスに高速・無料・VPC 内エンドポイントを提供する社内レートマイクロサービスです。ASP.NET Core Minimal API ならこれが 30 行のファイルです:
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();普段の API ゲートウェイの裏にデプロイすれば、フリート上のどのサービスからも GET /convert?from=USD&to=EUR&amount=99.95 をキャッシュヒット時 10ms 未満のレイテンシで呼べます。ブラウザ向けには同じデータが当社の通貨換算ツールでも利用できます。
C# で金額を正しく扱う
決済元帳を初めて監査する際に元が取れる、いくつかの防御的パターン:
- 常に
decimalを使い、doubleやfloatは使わない。0.1 + 0.2が浮動小数点で0.3にならないのは有名 — その 1 回の丸め誤差がチャージバック紛争につながりかねません。 - 表示時に丸める、計算時には丸めない。 すべての乗算で完全な精度を保ち、表示・永続化時のみ
Math.Round(value, 2, MidpointRounding.ToEven)を呼びます。 - 丸めモードを固定する。 銀行家丸め(
ToEven)は IEEE 754 と ISO 80000-1 における金融業界の標準です。 - 通貨を不透明なコードとして扱う。 ISO 4217 文字列を使い、すべての通貨が 2 桁の小数を持つと仮定しない。JPY は 0 桁、BHD は 3 桁です。エッジケースは当社の ISO 4217 ガイドで。
本番運用のベストプラクティス
趣味の統合と何年も運用できるシステムを分ける追加パターン:
- HTTP タイムアウトをきつく設定する。 デフォルトの 100 秒
HttpClient.Timeoutは高負荷時にスレッドプールを止めます。為替 API には 5 秒で十分。 - すべての外部呼び出しを
CancellationTokenで包む。 ASP.NET Core が無償で渡してくれます — 尊重すれば、切断されたリクエストが上流と通信し続けません。 - ステータスコードをログし、ボディはログしない。 為替 API のレスポンスは大きすぎてログに不向きで、Splunk に入れたくないレートデータを含むこともあります。
- クォータを監視する。 料金プランを比較し、CloudWatch / Application Insights で月次上限の 80% にアラートを設定。
- 実際に使う通貨だけをピンする。
symbols=EUR,GBP,JPYを送ると、レスポンスが約 10 KB から 200 バイト未満に縮みます。 - 積極的にキャッシュする。 レイテンシ敏感なチェックアウトは 30 秒キャッシュで動きますし、管理ダッシュボードは 5 分でも十分です。
プロバイダーを評価しているなら、当社の無料 vs 有料為替 API 比較が主要オプションの精度・稼働率・価格を概観します。
よくある質問
C# と .NET に最適な為替 API は?
クリーンな JSON と安定した稼働率を備えたあらゆる REST API は、IHttpClientFactory 経由でうまく統合できます。Finexly は言語非依存に設計されています — 同じエンドポイントが Node、Python、Go、Java、.NET クライアントから SDK ロックインなしで利用されます。詳しくは無料の為替 API 概要を参照してください。
Newtonsoft.Json と System.Text.Json はどちらを使うべき?
.NET 6 以降の新規コードには System.Text.Json。2〜3 倍速く、アロケーションも少なく、BCL の一部なので追加で管理するパッケージがありません。Newtonsoft.Json は今でも優秀でレガシーコードでは維持する価値がありますが、新規の C# 為替統合は System.Text.Json をデフォルトに。
NuGet SDK は必要? それとも HttpClient で十分? 手書きの Typed Client(約 50 行の C#)がほぼ常に正解です。キャッシュ、リトライ、テレメトリ、シリアライズを完全に制御でき、サードパーティ SDK のバージョンずれリスクを避けられます。本チュートリアルの例は本番に投入可能です。
.NET アプリで為替レートを更新する頻度は? ほとんどのユースケース — EC のチェックアウト、SaaS の課金、請求書 — では 30〜60 秒のキャッシュが安全です。トレーディングシステムは当然リアルタイムが必要ですが、それ以外では分単位の新鮮さはユーザーから見えず、API コストを劇的に削減できます。
Azure Functions や AWS Lambda で動かせる?
動かせます。分離プロセスの Azure Functions と、Microsoft.Extensions.Hosting を使う Lambda ハンドラーで IHttpClientFactory は同様に機能します。コールドスタートが重要なワークロードでは AOT(PublishAot=true)で publish すると 100ms 未満で起動します。
decimal は double より遅い?
decimal は 1 算術演算あたり double の約 10〜20 倍遅いですが、為替換算で行う乗算は高々数回です。実ワークロードで差を測定することはできません — そして金額に対する正しさの利得は譲れません。
まとめ
これで C# / .NET 為替レート API 統合のための完全な本番品質ブループリントが手に入りました — 強型モデル、IHttpClientFactory ベースのクライアント、Polly によるリトライとサーキットブレーカー、インプロセスキャッシュ、コンソール換算ツール、ASP.NET Core Minimal API。ここにあるすべてのパターンは Microsoft 公式ガイダンスから直に来ており、実際のフィンテック本番コードで実戦経験を積んでいます。
.NET アプリにリアルタイム為替レートを届ける準備はできましたか?Finexly の無料 API キーを取得 — クレジットカード不要。月 1,000 リクエストの無料枠から始めて、製品の成長に合わせてアップグレードできます。先に他のプロバイダーとの比較を見たいなら、為替 API 比較をどうぞ。
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 →