/v1/latest/USD は JSON としてレート、source、観測時刻を返します。金額の計算では Decimal を使って精度を保ちます。
- urllib は追加パッケージなしで使える。
- requests は認証、timeout、エラー処理をまとめやすい。
- forex-python は移行途中のアダプターとして使える。
- source、effective_at、data_updated_at を値と一緒に保存する。
- 過去データは日付指定または
/v1/rangeで取得する。
最初のコール、依存パッケージなし
標準ライブラリ以外は何も要らない。1回のコールならurllib.requestで足りる:
これはキーなしで動く。匿名コールはIPアドレスごとに上限があるため、簡単な試験を超えるなら無料キーを取得する。
requestsとAPIキーで
ほとんどのプロジェクトはすでにrequestsを使っている。キーはベアラートークンとして渡す:
無料キーで上限は月5,000コール・毎分10リクエストに上がる。Authorizationを設定できないプラットフォーム向けにX-API-Keyヘッダーも使える。
金額を換算する
レート一覧ではなく換算が必要なら/v1/convertを呼ぶ。レートと換算額が一緒に返る:
レートの鮮度を知る
表示する通貨については、sources[currency]でデータの種類、effective_at[currency]で観測時刻を確認してください。sourceはレスポンス全体で最も更新頻度の低いデータを表します。market_sessionは市場の取引時間帯、timestampはレスポンスの生成時刻であり、価格の観測時刻ではありません。
| フィールド値 | 意味 | 動くタイミング |
|---|---|---|
| source: live | 参考用のスポット市場レート | 取引週の日中(約60秒間隔) |
| source: ecb_daily | 公式な参照フィックス | 営業日に1回 |
| source: market_daily | ECB対象外の保存終値 | 日次市場データ |
| market_session: weekend | 土曜または日曜(インターバンク休場) | 参照フィックスは金曜値を維持、ライブフィードは停止 |
レートは参照、分析、表示向けに公開されている。ディーリングクォートではなく、取引の決済に使ってはならない。すべてのレスポンスのnoticeフィールドにその旨が記載されている。
過去の特定日を読む
1999年からはPlus/Proキーが必要。Freeは30日。レスポンスにはis_forward_filledが追加され、週末や祝日などその日にレートが公表されなかった場合にtrueになる:
日付範囲全体の系列を1コールで取るには、start_dateとend_dateを付けて/v1/rangeを使う。詳細は別のガイドで扱っている。
forex-python から移行する
forex-python は独自のプロバイダーを使うため、exchangerate.dev に自動接続するわけではありません。使い慣れた API を残す場合も、source と観測時刻を捨てないでください。
import jsonfrom decimal import Decimalimport urllib.requesturl = "https://api.exchangerate.dev/v1/latest/USD?symbols=EUR,GBP"with urllib.request.urlopen(url, timeout=10) as response: data = json.load(response, parse_float=Decimal)print(data["rates"]["EUR"])print(data["sources"]["EUR"], data["effective_at"]["EUR"])print(data["market_session"])# Install first: python -m pip install requestsimport osfrom decimal import Decimalimport requestsresponse = requests.get( "https://api.exchangerate.dev/v1/latest/USD", headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"}, timeout=10, allow_redirects=False,)if response.status_code == 429: raise RuntimeError(f"Rate limited; Retry-After: {response.headers.get('Retry-After')}")if 300 <= response.status_code < 400: raise RuntimeError("Unexpected redirect")response.raise_for_status()data = response.json(parse_float=Decimal)print(data["rates"]["GBP"], data["effective_at"]["GBP"])import osfrom decimal import Decimalimport requestsresponse = requests.get( "https://api.exchangerate.dev/v1/convert/USD/EUR/100", headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"}, timeout=10, allow_redirects=False,)if not 200 <= response.status_code < 300: raise RuntimeError(f"HTTP {response.status_code}; Retry-After: {response.headers.get('Retry-After')}")data = response.json(parse_float=Decimal)print(data["rate"], data["converted"])import osfrom decimal import Decimalimport requestsresponse = requests.get( "https://api.exchangerate.dev/v1/2026-03-14/USD?symbols=JPY", headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"}, timeout=10, allow_redirects=False,)if not 200 <= response.status_code < 300: raise RuntimeError(f"HTTP {response.status_code}; Retry-After: {response.headers.get('Retry-After')}")data = response.json(parse_float=Decimal)print(data["rates"]["JPY"], data["is_forward_filled"])