シートにキーを入れずに始める 最新レートのエンドポイントは匿名リクエストを受け付け、IPごとに毎分12回・毎時100回まで利用できる。このプライベートシートの例ではそれを使う。バインドされたApps Scriptプロジェクトはすべての編集者から見えるため、Bearerトークンを安全に置く場所ではない。共有シートやプロダクトでは、キーをシートの外に保存したサーバー側プロキシ経由でリクエストする。
シートからApps Scriptを開く 任意のGoogleスプレッドシートを開き、メニューから 拡張機能 → Apps Script をクリックする。新しいタブが開き、既定の function myFunction() {}が入っている。その中身は削除する — 次のステップで置き換える。
FXRATE関数を貼り付ける エディターの内容をこの関数で置き換える。exchangerate.devを匿名で呼び出し、 CacheServiceで各ペアを10分間キャッシュし、アクセス、クォータ、ペア検証の失敗には読めるマーカーを返す。
Code.gs copy
// google-sheets: Apps Script Code.gs
const 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
} 任意のセルで=FXRATE()を使う シートに戻り、空のセルに =FXRATE("USD","EUR")と入力する。最初の未キャッシュ呼び出しは数秒かかることがある。同じ関数はサポートされている任意のペアで動く — 2つの通貨コードを入れ替えるだけだ。円を使う例は次のとおり:
Sheets formula · pairs copy
// 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 円を基準にした換算も同じ書式で、 =FXRATE("JPY","USD")、 =FXRATE("JPY","EUR")、 =FXRATE("GBP","JPY")のように入力できる。内部では、各コールがベース通貨、絞り込んだペア、そして2つの鮮度フィールド — sourceと market_session — を返すため、その数値がどれくらい新しいかが常に分かる:
GET /v1/latest/USD?symbols=JPY copy
{
"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."
} AIアシスタントと一緒に作るなら
exchangerate.devはMCPサーバーと llms.txtを公開しているので、ClaudeやCursor、ChatGPTがライブのスキーマを読んでこの関数を書いてくれる。「fromとtoの通貨を受け取ってexchangerate.devのライブレートを返す、10分キャッシュ付きのFXRATEというGoogle Sheetsカスタム関数を作って」と頼めばよい。詳しくは MCPガイド を参照。 更新頻度と無料枠の収まり Apps Scriptはカスタム関数をタイマーで自動再実行しない。定期更新したい場合は時間主導型のトリガーを追加する:Apps Script左側のレールで トリガー → トリガーを追加 をクリックし、 FXRATEを選び、ソースは 時間主導型 、 分ベースのタイマー にする。10分キャッシュと十分な枠があるので、頻繁に触るブックでも安全に収まる:
月10,000コール、毎分12リクエスト、カード不要 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フィールドにその旨が記載されている。