For an invoice dated Saturday 10 October 2026, use the Friday 9 October 2026 EUR/USD rate of 1.1206: EUR 12,500 converts to USD 14,007.50. Request the previous seven days and take the last row on or before the invoice date, since weekends have no rate.
Do it with the API
- Set your API key Get a free key and keep it in the environment variable
NARWHAL_API_KEY. The script reads that variable and sends the key in theAuthorization: Bearer YOUR_KEYheader. Use the same key for Narwhal's REST API and MCP; the request here goes to the REST API. - Request the invoice date and preceding days Use
GET /v1/fx/rates/EUR/observationswithcurrencies=USD,start_dateandend_date. Set the end date to the invoice date and subtract seven days for the start date, as the script does. That range lets the response include an earlier day with a rate when the invoice falls on a weekend or holiday. - Read the last observation Run the Python script with the invoice date, base currency and quote currency. The function takes the last row in
observationsand returns both its date and its rate. Keep those together: the invoice date is the date you asked about, while the returned date identifies the observation used for the conversion.Python, the rate on or before an invoice dateimport os from datetime import date, timedelta import requests def rate_on(invoice: date, base: str, quote: str) -> tuple[str, float]: """The rate for the invoice date, or for the last day with a rate before it (weekends, holidays).""" start = invoice - timedelta(days=7) r = requests.get(f"https://api.narwhalapi.com/v1/fx/rates/{base}/observations", params={"currencies": quote, "start_date": start.isoformat(), "end_date": invoice.isoformat(), "limit": 100}, headers={"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}, timeout=10) r.raise_for_status() last = r.json()["observations"][-1] return last["date"], float(last["rates"][0]["rate"]) day, rate = rate_on(date(2026, 10, 10), "EUR", "USD") # 10 October 2026 is a Saturday print(day, rate, "-> EUR 12,500 =", round(12500 * rate, 2), "USD") - Multiply the invoice amount by the rate Use the returned rate to convert the invoice amount. For the invoice dated 10 October 2026, the script returns 9 October 2026 and a rate of 1.1206. Its printed result pairs the observation date with the converted amount. For hundreds of invoices, use the script with a cache of seven-day windows.
Do it in a sheet or without code
For a handful of invoices, use a sheet of pulled rates with a column of dates and a column of rates. Sort the dates in ascending order before applying the lookup. Put the invoice date in A2, and use dates and rates for the corresponding columns in the formula.
=LOOKUP(A2, dates, rates)The approximate match returns the rate for the latest date on or before A2. A Saturday invoice therefore uses the preceding day with a rate, without a separate weekend formula. Multiply the invoice amount by the returned rate, using rates for the currency direction you need. The sheet route uses the same rule as the script: select the last available observation up to the invoice date. State that weekend and holiday rule in your accounting policy.
Which option to use when
| Situation | Best option | Why |
|---|---|---|
| A handful of invoices | Lookup in a sheet of pulled rates | An approximate date match handles weekends |
| Hundreds of invoices | The script with a cache of seven-day windows | Use the date lookup across the invoices |
| A statutory rate | The authority's published rate | Narwhal supplies market daily observation rates |
Which rate your tax authority or auditor accepts is part of your accounting policy. That policy may call for the invoice date, payment date, a month average or a central bank rate. Establish which one applies before using the date lookup. Narwhal supplies market daily observation rates; when the policy requires a statutory rate, use the authority's published rate. The invoice-date calculation here applies when your policy calls for that date and these market observations.
Which currency pair direction should I request?
Request the invoice currency as the base and the currency you want to convert into as the quote. For an invoice held in EUR and converted to USD, request EUR as the base with USD as the quote. The returned rate is USD per EUR, so multiply the EUR invoice amount by that rate.
The base appears in the observations endpoint path, and the quote goes in the currencies parameter. In the Python call, those same choices are the base and quote arguments after the invoice date. Keep that direction when you put the rates into a sheet, so the lookup returns USD per EUR for a EUR invoice.
Can I use the convert endpoint for an invoice date?
Use observations for a past invoice date because the convert endpoint has no date parameter. GET /v1/fx/convert?from=EUR&to=USD&amount=12500 converts at the current rate only. It does not select the observation that applied on the invoice date. For a dated invoice, request the observations range and multiply the amount by the last rate on or before that date.
Why does the response start after my requested date?
A start date before your plan's history window is moved up to the window's first day, so the response begins later than requested. For an invoice lookup, the range needs to include the invoice date and an earlier observation when that date has no rate, subject to plans and limits.
403 plan_upgrade_required applies when the whole range ends before the history window, or when you request a single date or period outside it. An earlier start date alone does not trigger that error. Errors use application/problem+json with status, code, detail and request_id; read the error detail if your request is outside the window.
Worked example: EUR 12,500 invoiced on 10 October 2026
Start with the invoice date, Saturday 10 October 2026, and the amount, EUR 12,500. Request the seven-day window up to that date. Since the invoice falls on a weekend, take the last earlier observation: Friday 9 October 2026. For that date, the EUR/USD rate is 1.1206 USD per EUR.
At the 9 October 2026 rate, multiply EUR 12,500 by 1.1206 to get USD 14,007.50 for the invoice dated 10 October 2026. The script prints the observation date as 2026-10-09 and the converted amount as 14007.5 USD.
| Step | Value |
|---|---|
| Invoice date | 10 October 2026, Saturday |
| Observation date | 9 October 2026, Friday |
| EUR/USD rate on 9 October 2026 | 1.1206 USD per EUR |
| Convert the 10 October 2026 invoice | EUR 12,500 × 1.1206 = USD 14,007.50 |
Published .
