How to get a historical exchange rate for a date

One formula gets an FX rate for a date. Choosing the rate — a central bank's fixing or a market close, which hour, what on a holiday — is the part that matters.

In Google Sheets, GOOGLEFINANCE with CURRENCY: and a date range returns daily rates, with no open, high or low and dates treated as noon UTC. In Excel, STOCKHISTORY takes a Currencies data type. In Python, the ECB and FRED publish official daily rates free. None of them is the rate your broker applied. Decide which rate the job needs, which way round the pair is quoted, and what happens on a weekend or a holiday.

The short way

A rate "on a date" is three decisions before it is a formula: whose rate (a central bank's fixing, or some vendor's market close), what hour of that day, and what to do when the day has no rate. Make them once and write them next to the column. Then:

Google Sheets. Google's own example sheet builds the symbol as CURRENCY: followed by the two currency codes and asks for "price" over a date range. Asking for a range rather than one day gets you the last rate on or before the date even when the date is a Saturday:

=LET(h, GOOGLEFINANCE("CURRENCY:EURUSD", "price", A2 - 7, A2), INDEX(h, ROWS(h), 2))

With the date in A2, that is the last value in the week up to A2; change the final 2 to 1 to see which day it came from. Google's help page says currencies "don't have trading windows", so open, high, low and volume do not return for them — a currency is one number a day here. It also says Google treats the dates you pass as noon UTC, so a value can land a day off.

Excel. Type EUR/USD in A1, select it, and choose the Currencies data type on the Data tab — Microsoft's help says the pair is written from-currency, slash, to-currency in ISO codes. Then, with the date in B1:

=TAKE(STOCKHISTORY(A1, B1 - 7, B1, 0, 0, 0, 1), -1)

returns the date and the close of the last day on or before B1, without headers. Microsoft's Excel blog announcing STOCKHISTORY says it accepts currency pairs, and that the easiest way to pass one is a cell holding the data type. It needs a Microsoft 365 subscription, and the currencies help page says pairs are only available to Microsoft 365 accounts on worldwide multi-tenant tenants. Its other limits are on what to use instead of the Stocks data type and STOCKHISTORY.

Python, with an official rate. The ECB Data Portal needs no key. This returns the euro reference rate for a date, or for the last ECB working day before it:

import pandas as pd

def ecb_rate(currency: str, day: str) -> tuple[str, float]:
    """Units of `currency` per one euro on `day`, or on the last ECB working day before it."""
    start = (pd.Timestamp(day) - pd.Timedelta(days=10)).date()
    url = ("https://data-api.ecb.europa.eu/service/data/EXR/"
           f"D.{currency}.EUR.SP00.A?startPeriod={start}&endPeriod={day}&format=csvdata")
    rates = pd.read_csv(url, usecols=["TIME_PERIOD", "OBS_VALUE"])
    last = rates.iloc[-1]
    return last["TIME_PERIOD"], float(last["OBS_VALUE"])

print(ecb_rate("USD", "2026-04-03"))   # Good Friday: ('2026-04-02', 1.1525)

What the options are

The euro reference rates. The ECB Data Portal serves them as an SDMX series keyed EXR/D.{currency}.EUR.SP00.A, about 30 currencies against the euro, one value per working day from 1999. The ECB's page says they come from a concertation between central banks that normally takes place around 14:10 CET and are published around 16:00 CET; the series' own title in the API reads "2.15 pm (C.E.T.)". They are quoted as units of the other currency per euro, so USD returns dollars per euro. In Sheets, =INDEX(IMPORTDATA(url), 2, 8) on the same URL returns the value — it is the eighth column of the CSV.

The Federal Reserve's noon rates. FRED carries the Fed's H.10 release as daily series — DEXUSEU for the euro, DEXJPUS for the yen — whose notes describe them as noon buying rates in New York City for cable transfers payable in foreign currencies. The API takes a free key at 120 requests a minute, FRED ships a free Excel add-in, and the CSV behind each series page's download link needs no key at all, which makes it a one-cell lookup in Sheets:

=INDEX(IMPORTDATA("https://fred.stlouisfed.org/graph/fredgraph.csv?id=DEXUSEU&cosd=2026-03-31&coed=2026-03-31"), 2, 2)

H.10 is a weekly release: on 8 October 2026 the newest DEXUSEU value was for 2 October, published on 5 October. A rate for yesterday is not there yet.

A vendor's daily close. Alpha Vantage's FX_DAILY returns open, high, low and close per day for any pair in its currency list, on the free key, and datatype=csv makes it readable by IMPORTDATA or Power Query's From Web; outputsize=full returns the whole history in one call, which matters at 25 requests a day. Its documentation does not say at what hour the daily bar closes.

A rate at a moment. Twelve Data's exchange-rate endpoint takes a date that can be a day or a date and time, read in the time zone you pass — the nearest thing here to the rate at the minute a trade was filled. The free key allows 800 requests a day and is licensed for internal non-display use only; anything shown to other people is a paid plan.

Another central bank's fixing. Some tax authorities and contracts name their own central bank's rate. FXMacroData collects official reference rates and fixings for 22 currencies, such as Brazil's PTAX and the Central Bank of Iceland's 16:00 rate, in one schema, from $50 a month; its free tier is US data only.

Where this breaks

A reference rate is not the rate you got. The ECB says its rates are published for information purposes only and that using them for transactions is strongly discouraged. Your broker converted at its own rate, with its own margin inside it, at its own moment. For what a trade or a dividend actually cost or paid, the confirmation's rate is the record; the reference rate is for valuing things nobody converted. Tracking foreign dividends works through what that means for a ledger.

Two official rates for the same day disagree. For 31 March 2026 the ECB's reference rate was 1.1498 dollars per euro and the Fed's H.10 noon rate 1.1518. On €100,000 that is $200 between two answers to "the rate on 31 March", both correct, nine hours apart. Pick one source and one hour per job and do not mix them inside a ledger.

Weekends and holidays are different holes in each source. The ECB publishes nothing on TARGET closing days: in 2026 there is no row for Good Friday, 3 April, or Easter Monday, 6 April, while H.10 has both (1.1523 and 1.1545). The Fed skips US holidays instead, and FRED's CSV keeps the date with an empty value — Thanksgiving, 27 November 2025, is a row with nothing in it, which a spreadsheet reads as zero if you let it. A lookup for the exact date fails, or a join silently drops the row. The usual rule is the last published rate before the date; whichever rule you use, apply it everywhere.

Which way round. EURUSD at 1.15 is dollars per euro. FRED's DEXUSEU is dollars per euro and its DEXJPUS is yen per dollar; the H.10 release marks the Australian dollar, the euro, the New Zealand dollar and the pound as dollars per unit and quotes every other currency per dollar. The IRS's yearly average table is foreign currency per dollar, which is why its instruction is to divide a foreign amount by the rate. Get it backwards and €10,000 at 1.15 becomes $8,696 instead of $11,500.

Cross rates are arithmetic, not observations. The ECB publishes everything against the euro and H.10 everything against the dollar. A yen-per-dollar figure from the ECB is two euro rates divided — consistent, and nobody's quote.

GOOGLEFINANCE has limits beyond the date. Google's help says historical data cannot be read through the Sheets API or Apps Script — a script gets #N/A — and that the data is not for financial-industry professional use. What to use instead of GOOGLEFINANCE covers both.

A tax authority's own table is not always a rate for your date. HMRC's monthly exchange rates are for customs and VAT: published on the penultimate Thursday of each month, they apply to the following calendar month and reflect rates at midday the day before publication, so HMRC's rate "for" 15 October is a September rate. For capital gains, HMRC's manual converts the cost at the acquisition date and the proceeds at the disposal date, and says it does not prescribe a reference point but expects a reasonable and consistent method. The IRS says it has no official exchange rate, generally accepts any posted rate used consistently, and asks for the spot rate when you receive, pay or accrue an item. Which of these applies to you is the tax authority's material, not this page's.

If you outgrow this

When it is a ledger, not a date. Calling an API once per transaction spends a free quota in an afternoon. Download the series once and join it to the transactions by the rule you chose:

trades = trades.assign(date=trades["date"].dt.as_unit("ns")).sort_values("date")
rates = rates.assign(date=rates["date"].dt.as_unit("ns")).sort_values("date")   # columns: date, rate
priced = pd.merge_asof(trades, rates, on="date", direction="backward")   # last rate on or before

The two as_unit calls are for pandas 3.0, released on 21 January 2026, which parses date strings to microseconds where pandas 2 used nanoseconds. merge_asof refuses keys of two different resolutions with a MergeError, and a ledger parsed under pandas 3 meeting a rate series stored at nanoseconds by pandas 2 is exactly that case. Both snippets on this page ran under pandas 3.0.6 on 9 October 2026: the ECB function returned the Good Friday value shown, and the join returned the last rate on or before each trade.

When the hour matters more than the day — a trade at 15:30 New York against a fixing set at 14:10 Frankfurt — the forex data collection separates reference rates, vendor quotes and tick history, and says which is for what.

When the rate is one series among many, pulling an economic data series covers FRED, the ECB and the other official sources in general, and the rest of the shelf is macro and economic data. Why a tracker's converted value never quite matches the broker's is in why your tracker and your broker disagree.

The tools that do this

In the order this page recommends trying them. Paid placement does not affect this order.

  1. ECB Data Portal API

    The euro reference rates, keyless, as CSV — about 30 currencies against the euro from 1999, one fixing per working day around 14:10 CET.

    Euro reference rates, yield curves and €STR over keyless SDMX — with vintages.

    FreeFree tier

  2. FRED

    The Federal Reserve's H.10 noon buying rates in New York as daily series, free key or keyless CSV, published weekly. Some quoted per dollar, some in dollars.

    The Fed's macro database — 851,100 series, a free key and 120 requests a minute.

    FreeFree tier

  3. Alpha Vantage

    Daily open, high, low and close for a pair on the free key, as CSV that IMPORTDATA or Power Query reads. 25 requests a day.

    The API most people's first script talks to — free key, wide coverage, hard rate limits.

    $49.99/moFree tier

  4. Twelve Data

    A rate at a date or at a minute, in the time zone you name — the closest to the moment a trade happened. Free key licensed for non-display use only.

    Global stocks, forex and crypto over REST and WebSocket, billed in credits per minute.

    $29/moFree tier

  5. FXMacroData

    Other central banks' fixings, such as Brazil's PTAX, for 22 currencies in one schema. From $50 a month; the free tier is US data only.

    Release-timestamped macro data for 22 currencies, with a calendar and COT, from $50.

    $10/moFree tier

FAQ

Why is the GOOGLEFINANCE rate for my date a day off?

Google's help page says it treats dates passed to GOOGLEFINANCE as noon UTC and that values can shift by a day as a result. It does not document what time of day a currency's daily value is taken. Show the date column the function returns next to the rate, so you can see which day you actually got, rather than reading only the number.

Why does the ECB have no rate for my date?

Because the ECB sets its reference rates only on working days and skips TARGET closing days. Weekends and TARGET holidays have no row at all — in 2026 there is nothing for Good Friday, 3 April, or Easter Monday, 6 April. The usual rule is the last published rate before the date, but it is a rule you choose and should apply the same way every time.

Which exchange rate should I use for my taxes?

The tax authority's own guidance decides, and it often leaves the choice to you within limits. The IRS says it has no official exchange rate and generally accepts any posted rate used consistently, converting at the spot rate when you receive, pay or accrue an item. HMRC's capital gains manual converts cost at the acquisition date and proceeds at the disposal date, and asks for a reasonable and consistent method. This page explains what each rate is, not which one your return needs.

Is the ECB reference rate the mid-market rate?

It is a fixing, not a quote anyone dealt at. The ECB sets it in a daily concertation between central banks around 14:10 CET, publishes it around 16:00 CET, and says the rates are for information purposes only and that using them for transactions is strongly discouraged. It is close to the market at that moment and nothing more.

Sources

  1. GOOGLEFINANCE — Google Docs Editors Help — Google, read
  2. STOCKHISTORY function — Microsoft, read
  3. Get a currency exchange rate — Microsoft, read
  4. Announcing STOCKHISTORY — Microsoft Excel Blog, . Read on 8 October 2026; the STOCKHISTORY help page is silent on currency pairs, so this post is still Microsoft's statement that they work.
  5. Euro foreign exchange reference rates — European Central Bank, read
  6. U.S. Dollars to Euro Spot Exchange Rate (DEXUSEU) — Federal Reserve Bank of St. Louis, read
  7. Foreign Exchange Rates — H.10 Weekly — Board of Governors of the Federal Reserve System,
  8. Alpha Vantage API Documentation — FX_DAILY — Alpha Vantage, read
  9. Exchange rate — API reference — Twelve Data, read
  10. Yearly average currency exchange rates — Internal Revenue Service, read
  11. CG78310 — Foreign currency: assets acquired or sold for currency — HM Revenue & Customs, read
  12. Check foreign currency exchange rates — UK Integrated Online Tariff — HM Revenue & Customs, read
  13. What's new in 3.0.0 (January 21, 2026) — pandas development team,

The catalogue next door

This page names a handful of cards. The rest of them are in Macro & Economic Data APIs, each filled in against the same schema, with the fields to narrow it yourself.

Last updated . Corrected in place: this is a reference page, not a dated post.

Report an error on this page

Quote the line and say what it should be.

The vendor’s page, filing or documentation that says otherwise.

Only if you want to hear back. Never published.

Read by a person. It fixes a fact and moves nothing else.