セルの中だけで完結するなら GOOGLEFINANCE が最短です。アプリと同じデータ取得や認証が必要になったら、Apps Script を代替手段として検討してください。
- 現在値は GOOGLEFINANCE の式で取得する。
- 履歴データは Sheets API や Apps Script から直接読めない。
- Apps Script の FXRATE は JSON とキャッシュを扱うための代替手段。
- 共有シートの Apps Script に API キーを書かない。
- 表示時は取得時刻と source を残す。
シートにキーを入れずに始める
最新レートのエンドポイントは匿名リクエストを受け付け、IPごとに毎分10回・毎時100回まで利用できる。このプライベートシートの例ではそれを使う。バインドされたApps Scriptプロジェクトはすべての編集者から見えるため、Bearerトークンを安全に置く場所ではない。共有シートやプロダクトでは、キーをシートの外に保存したサーバー側プロキシ経由でリクエストする。
シートからApps Scriptを開く
任意のGoogleスプレッドシートを開き、メニューから拡張機能 → Apps Scriptをクリックする。新しいタブが開き、既定のfunction myFunction() {}が入っている。その中身は削除する、次のステップで置き換える。
FXRATE関数を貼り付ける
エディターの内容をこの関数で置き換える。exchangerate.devを匿名で呼び出し、CacheServiceで各ペアを10分間キャッシュし、アクセス、クォータ、ペア検証の失敗には読めるマーカーを返す。
任意のセルで=FXRATE()を使う
シートに戻り、空のセルに=FXRATE("USD","EUR")と入力する。最初の未キャッシュ呼び出しは数秒かかることがある。同じ関数はサポートされている任意のペアで動く、2つの通貨コードを入れ替えるだけだ。円を使う例は次のとおり:
円を基準にした換算も同じ書式で、=FXRATE("JPY","USD")、=FXRATE("JPY","EUR")、=FXRATE("GBP","JPY")のように入力できる。内部では、各コールがベース通貨、絞り込んだペア、そして2つの鮮度フィールド、sourceとmarket_session:を返すため、その数値がどれくらい新しいかが常に分かる:
exchangerate.devはMCPサーバーとllms.txtを公開しているので、ClaudeやCursor、ChatGPTがライブのスキーマを読んでこの関数を書いてくれる。「fromとtoの通貨を受け取ってexchangerate.devのレートを返す、10分キャッシュ付きのFXRATEというGoogle Sheetsカスタム関数を作って」と頼めばよい。詳しくはMCPガイドを参照。
更新頻度と無料枠の収まり
Apps Scriptはカスタム関数をタイマーで自動再実行しない。定期更新したい場合は時間主導型のトリガーを追加する:Apps Script左側のレールでトリガー → トリガーを追加をクリックし、FXRATEを選び、ソースは時間主導型、分ベースのタイマーにする。10分キャッシュと十分な枠があるので、頻繁に触るブックでも安全に収まる:
- 月5,000コール、毎分10リクエスト、カード不要
CacheServiceが10分間はキャッシュ済みのレートを返すため、編集を繰り返してもコールは増えない- ペアの多いシートが毎分の上限に触れそうなら
CACHE_TTLを上げる /v1/latest、/v1/convert、/v1/range、/v1/{date}/{base}をカバー
| コード | 症状 | 対処 |
|---|---|---|
| 401 | セルに#AUTH_ERRORが表示される | キーが未設定か無効。ダッシュボードからCode.gs冒頭のAPI_KEYにコピーし直す。 |
| 429 | セルに#RATE_LIMITが表示される | 毎分の上限を超えた。通常はキャッシュが防ぐ。CACHE_TTLを上げるか、/pricingで上位プランへ。 |
| — | セルに#NO_PAIRが表示される | クォート通貨がレスポンスに含まれていない。コードがサポート対象のISO通貨か確認する(例:JYPではなくJPY)。 |
| — | セルがLoading…のまま | Apps Scriptに外部フェッチの承認が必要。エディターのツールバーから関数を一度実行して確認ダイアログを出し、承認する。 |
これらのレートはモデル、分析、表示向けに公開されている。ディーリングクォートではなく、取引や送金の決済に使ってはならない。すべてのレスポンスのnoticeフィールドにその旨が記載されている。
まず GOOGLEFINANCE を使う
セルには =GOOGLEFINANCE("CURRENCY:USDEUR")、期間指定には開始日・終了日・DAILY を指定します。表示値には遅延があり、履歴データは Sheets API や Apps Script から取得できません。
=GOOGLEFINANCE("CURRENCY:USDEUR")=GOOGLEFINANCE("CURRENCY:USDEUR", "price", DATE(2026, 1, 1), DATE(2026, 1, 31), "DAILY")// google-sheets: Apps Script Code.gsconst CACHE_TTL = 600; // Cache successful responses for 10 minutes./** * Returns an indicative exchange rate for one currency pair. * @param {string} from Three-letter base currency code. * @param {string} to Three-letter quote currency code. * @return {number|string} The rate or a readable error marker. * @customfunction */function FXRATE(from, to) { from = (from || 'USD').toString().trim().toUpperCase(); to = (to || 'EUR').toString().trim().toUpperCase(); if (!/^[A-Z]{3}$/.test(from) || !/^[A-Z]{3}$/.test(to)) return '#NO_PAIR'; const cacheKey = `fx_${from}_${to}`; const cache = CacheService.getScriptCache(); const cached = cache.get(cacheKey); if (cached) return Number(cached); const url = `https://api.exchangerate.dev/v1/latest/${from}?symbols=${to}`; let resp; try { resp = UrlFetchApp.fetch(url, { muteHttpExceptions: true }); } catch { return '#FETCH_ERROR'; } const code = resp.getResponseCode(); if (code === 401 || code === 403) return '#ACCESS_ERROR'; if (code === 429) return '#RATE_LIMIT'; if (code !== 200) return '#ERROR'; let data; try { data = JSON.parse(resp.getContentText()); } catch { return '#INVALID_RESPONSE'; } const rate = data && data.rates && data.rates[to]; if (rate == null) return '#NO_PAIR'; if (typeof rate !== 'number' || !Number.isFinite(rate) || rate <= 0) return '#INVALID_RESPONSE'; cache.put(cacheKey, String(rate), CACHE_TTL); return Number(rate); // coerce so cells sum and sort}// Euro per US dollar=FXRATE("USD", "EUR") // 0.9245// Japanese yen per US dollar=FXRATE("USD", "JPY") // 157.32// British pound per euro=FXRATE("EUR", "GBP") // 0.8531// Indonesian rupiah per US dollar=FXRATE("USD", "IDR") // 16285.0{ "result": "success", "base": "USD", "source": "live", "market_session": "open", "timestamp": "2026-06-29T09:14:02Z", "data_updated_at": "2026-06-29T09:14:00Z", "rates": { "JPY": 157.32 }, "sources": { "JPY": "live" }, "notice": "Indicative rates, not for settlement."}=FXRATE("USD", "EUR")