Month-End FX Controls in Google Sheets and Excel: Historical Pulls, Variance Checks, and Approval Tabs
Finance teams do not lose time on exchange rates because the math is hard. They lose time because the control design is sloppy. One person pastes a rate from a browser, another recalculates an invoice in Excel, a third team reruns the close workbook in Google Sheets, and nobody can explain which rate should survive into the final reporting pack. The fix is not another dashboard. It is a workbook with clear rules.
What is verified in the current public product surface
The current public API surface is enough to build a finance-owned spreadsheet workflow without pretending a native spreadsheet add-on exists. Currency-Exchange.app documents rate lookup, conversion, historical date parameters, currency list/details, usage statistics, and usage export endpoints. That gives you the building blocks for workbook tabs, approval logs, and close-period review.
What is not clearly documented as a public native feature matters just as much. The current public spec does not publish a native Google Sheets add-on, native Excel connector, or native webhook-based spreadsheet refresh. This workflow is deliberately API-based: Apps Script, Power Query, Office Scripts, or a generic scheduler make the HTTP calls, and your workbook stores the control evidence.
The six tabs that keep the workbook explainable
| Tab | Why it exists | Data rule |
|---|---|---|
| Pairs | Approved source and target currencies for the close run. | Use the list-currencies endpoint to load only active codes and metadata your team has approved for reporting. |
| Live Rates | Current exposure and operational checkpoints before close. | Use live calls for quote, payment, or billing events that genuinely need current market data. |
| Historical Rates | Date-specific rates for period close, revenue review, refunds, and variance investigation. | Use the date parameter on the documented rate or conversion endpoints and store the requested date beside every row. |
| Variance Checks | Compare quote, invoice, settlement, and reporting rates in one place. | Track original amount, converted amount, exchange rate, rateTime, and the business event that chose the rate. |
| Approvals | Owner sign-off, rerate reason, and refresh cutoff before figures are sent to ERP or the close binder. | Record approver, approved timestamp, rerate reason, and the exact workbook refresh run used for the final numbers. |
| Usage Review | Operational evidence for finance review and budget control. | Import API usage stats or the usage export file for the close period so finance can reconcile spreadsheet pulls against billed usage. |
Step-by-step workflow for a finance-owned close pack
- 1. Load approved currencies first. Pull the active currency set from the public list endpoint and stop the workbook from accepting ad hoc codes that finance never approved.
- 2. Separate live review from historical close. Keep the live tab for operational pricing or payment questions. Keep the historical tab for period-end work. If both live in one sheet, people will overwrite one with the other.
- 3. Store the requested date and the returned rateTime. The requested date explains why the row exists. The returned timestamp explains what the API actually gave you. You need both when variance questions appear later.
- 4. Build one variance tab, not five side calculations. Compare quote, invoice, settlement, and reporting rates in a single place. That is where you decide whether a difference is immaterial or needs an adjustment entry.
- 5. Add an approval tab before numbers leave the workbook. Finance should sign off on the refresh run, the historical date rule, and any rerates before the workbook feeds ERP uploads, invoices, or the close binder.
- 6. Pull usage evidence into the workbook. Close control is not only about rates. It is also about showing that the spreadsheet refreshes and backfills match the API usage the business paid for during the period.
Technical implementation: cURL, Apps Script, and Office Scripts
1. Pull active currencies for the workbook
curl "https://api.currency-exchange.app/v1-list-currencies?active=true&pageSize=300" \
-H "x-api-key: YOUR_API_KEY"2. Pull a historical close rate
curl "https://api.currency-exchange.app/v1-get-currency-exchange-rate?from=USD&to=EUR&date=2026-03-31" \
-H "x-api-key: YOUR_API_KEY"3. Review period usage
curl "https://api.currency-exchange.app/v1-get-api-usage?from=2026-03-01&to=2026-03-31&service=currency" \
-H "x-api-key: YOUR_API_KEY"4. Refresh the historical tab in Google Sheets
function refreshHistoricalRates() {
const sheet = SpreadsheetApp.getActive().getSheetByName('Historical Rates');
const apiKey = PropertiesService.getScriptProperties().getProperty('FX_API_KEY');
const rows = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3).getValues();
rows.forEach((row, index) => {
const [from, to, date] = row;
if (!from || !to || !date) return;
const url = 'https://api.currency-exchange.app/v1-get-currency-exchange-rate'
+ '?from=' + encodeURIComponent(from)
+ '&to=' + encodeURIComponent(to)
+ '&date=' + encodeURIComponent(date);
const response = UrlFetchApp.fetch(url, {
headers: { 'x-api-key': apiKey },
muteHttpExceptions: true,
});
if (response.getResponseCode() !== 200) {
sheet.getRange(index + 2, 7).setValue('ERROR ' + response.getResponseCode());
return;
}
const data = JSON.parse(response.getContentText());
sheet.getRange(index + 2, 4).setValue(data.exchangeRate);
sheet.getRange(index + 2, 5).setValue(data.rateTime);
sheet.getRange(index + 2, 6).setValue(new Date().toISOString());
sheet.getRange(index + 2, 7).setValue('OK');
});
}5. Refresh the same pattern in Excel Online
type FxRow = {
from: string;
to: string;
requestedDate: string;
};
async function main(workbook: ExcelScript.Workbook) {
const sheet = workbook.getWorksheet('Historical Rates');
const usedRange = sheet.getUsedRange();
const values = usedRange.getValues();
for (let row = 1; row < values.length; row += 1) {
const [from, to, requestedDate] = values[row] as FxRow[];
if (!from || !to || !requestedDate) {
continue;
}
const url = new URL('https://api.currency-exchange.app/v1-get-currency-exchange-rate');
url.searchParams.set('from', String(from));
url.searchParams.set('to', String(to));
url.searchParams.set('date', String(requestedDate));
const response = await fetch(url, {
headers: { 'x-api-key': 'YOUR_API_KEY' },
});
const data = await response.json() as {
exchangeRate: number;
rateTime: string;
cached?: boolean;
};
sheet.getRangeByIndexes(row, 3, 1, 3).setValues([[
data.exchangeRate,
data.rateTime,
new Date().toISOString(),
]]);
}
}6. Turn the workbook into a variance check
type VarianceRow = {
documentId: string;
quoteRate: number;
invoiceRate: number;
settlementRate: number;
};
function buildVarianceRow(row: VarianceRow) {
const quoteVsInvoice = row.invoiceRate - row.quoteRate;
const invoiceVsSettlement = row.settlementRate - row.invoiceRate;
return {
documentId: row.documentId,
quoteVsInvoice,
invoiceVsSettlement,
material: Math.abs(invoiceVsSettlement) >= 0.005,
};
}None of these examples assume a native spreadsheet integration. They are HTTP-based control patterns. That is exactly why they are durable: the same logic can move from Google Sheets to Excel, then into a no-code scheduler or warehouse later without changing the underlying rate policy.
When to use Google Sheets, Excel, or a scheduler
| Tool | Best for | Control note |
|---|---|---|
| Google Sheets + Apps Script | Finance-led workbooks that already live in Google Workspace. | API-based pattern. Use time-driven triggers or a manual close-run button. No native connector is verified. |
| Excel + Power Query or Office Scripts | Teams working in Microsoft 365 or with workbook-driven controls. | Use Power Query for read-heavy pulls and Office Scripts when you need a repeatable refresh action in Excel Online. |
| n8n, Make, or Power Automate HTTP step | Teams that want a scheduler to refresh tabs, write CSVs, or alert finance when rates fail validation. | Treat it as a generic HTTP orchestration layer, not as a native product integration. |
Where this workbook fits in the broader workflow
The workbook is not the source of truth for every workflow. It is a control layer that helps finance review the rates already used in quotes, invoices, and payment flows. Use live conversion close to customer-facing events. Use historical dates for reporting and reconciliation. Use the workbook to make those decisions explicit and reviewable.
If the process grows beyond spreadsheet scale, the same structure maps cleanly into the warehouse patterns in the currency data warehouse guide and the policy model in the governance playbook. The workbook becomes your short-term control surface, not a dead end.
Useful internal references for finance and operations
- Historical exchange rate data for finance teams for close-period rate rules and audit trails.
- Currency master data for finance ops if your workbook still has hardcoded symbols, decimals, or ISO codes.
- Exchange rate API governance for usage-export ownership, quota review, and spend controls.
- Public API reference for the documented endpoints used in the workbook.
FAQ
Should the workbook store both live and historical rates?
Yes, but in separate tabs with explicit rules. Live tabs support operational review. Historical tabs support close, audit, and variance analysis. Combining them in one uncontrolled range is how finance loses track of which rate was approved.
How often should a month-end workbook refresh?
Daily or once per close cycle is usually enough for historical tabs. Live tabs can refresh more frequently during active quote or payment review, but the team should still define a close cutoff after which values do not change without approval.
What should an approval tab capture?
Capture the workbook refresh timestamp, the requested historical date, the approver, any rerate reason, and the exact usage export file or run identifier tied to the close pack.
Where do no-code tools fit in this workflow?
Use them as schedulers, alerting layers, or file movers. The core control remains API-based: the spreadsheet or workbook should still store the chosen rate, timestamp, date, and approval context.
Give finance a workbook that can defend itself
A spreadsheet is not a risk by itself. An undocumented spreadsheet is. Start with the documented rate, historical, metadata, and usage endpoints. Add approval rules and variance checks. Then use the current pricing page to model the cost of refreshes and backfills before you scale the workflow into ERP, BI, or a warehouse.