ブログに戻る

C# / .NET 為替レート API 完全統合チュートリアル(2026)

V
Vlado Grigirov
May 26, 2026
C# .NET Currency API Exchange Rates Tutorial Fintech ASP.NET Core

Microsoft スタックの上に決済バックエンド、SaaS の課金エンジン、企業向けの価格サービスを構築しているなら、今週リリースできる最もインパクトの大きいタスクの一つC# / .NET の為替レート API 統合です。モダンな .NET には必要なものがすべて標準で揃っています — HttpClientIHttpClientFactorySystem.Text.JsonIMemoryCache、そして一級市民の依存性注入 — それなのに大半のチュートリアルはいまだに、本番のコードレビューを絶対通らない 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 はデファクトの回復力ライブラリ。 リトライ、サーキットブレーカー、タイムアウトが AddPolicyHandler 1 行で書けます。
  • Minimal API と AOT。 30 行で小さなレート用マイクロサービスを立ち上げるのが現実的で、コールドスタートはミリ秒です。

他のスタックを検討中なら Node.jsPythonGoPHP の姉妹ガイドがあります。本記事は 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.csproj

Finexly の 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 の代わりに使いましょう。

これで dynamicDictionary<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.Memory

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);
        })!;
    }
}

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=true

ASP.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 を使い、doublefloat は使わない。 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 未満で起動します。

decimaldouble より遅い? decimal は 1 算術演算あたり double の約 10〜20 倍遅いですが、為替換算で行う乗算は高々数回です。実ワークロードで差を測定することはできません — そして金額に対する正しさの利得は譲れません。

まとめ

これで C# / .NET 為替レート API 統合のための完全な本番品質ブループリントが手に入りました — 強型モデル、IHttpClientFactory ベースのクライアント、Polly によるリトライとサーキットブレーカー、インプロセスキャッシュ、コンソール換算ツール、ASP.NET Core Minimal API。ここにあるすべてのパターンは Microsoft 公式ガイダンスから直に来ており、実際のフィンテック本番コードで実戦経験を積んでいます。

.NET アプリにリアルタイム為替レートを届ける準備はできましたか?Finexly の無料 API キーを取得 — クレジットカード不要。月 1,000 リクエストの無料枠から始めて、製品の成長に合わせてアップグレードできます。先に他のプロバイダーとの比較を見たいなら、為替 API 比較をどうぞ。

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 →

この記事を共有する