14.6 million official exchange rates from 107 central banks and tax authorities, now on Hugging Face
If you have ever needed "the ECB rate on the invoice date" for a tax return, a customs form, a transfer-pricing file or an audit, you know the problem. The number exists. It is public. And it is scattered across a hundred central-bank websites, each with its own PDF, XML feed, Excel download or HTML table, each with its own idea of which way round a rate should be quoted.
We spent most of this year collecting those tables into one pipeline. As of this week the full history is a public dataset on Hugging Face:
huggingface.co/datasets/AllRates/central-bank-exchange-rates
| Rows | 14,612,276 |
|---|---|
| Institutions | 103 central banks + 4 tax authorities |
| Earliest date | 1914 (Swiss National Bank) |
| Currencies | 215 base, 202 quote |
| Refresh | Daily, from the GitHub source repository |
| License | CC BY 4.0 |
| Size | 515 MB CSV, auto-converted to Parquet |
Why "official" rates and not market rates
Most exchange-rate datasets are market data: a mid-market snapshot from some aggregator at some time of day. That is the right thing for a price display. It is the wrong thing for anything a regulator will look at.
Tax offices, customs agencies and accounting standards usually name a publisher. HMRC publishes monthly rates for UK VAT and customs. The ECB reference rate is the default for euro-area statutory reporting. The Reserve Bank of India reference rate is the benchmark for statutory rupee conversions. The Reserve Bank of Australia, Bank of Canada, Banco Central do Brasil and dozens of others publish a daily fixing that is the rate for that jurisdiction, regardless of what the interbank market did five minutes later.
Those numbers are not derivable from market data. They have to be collected from the source, on the source's schedule, in the source's convention. That is what this dataset is. (If you want the same rates as an API rather than a download, that is the central bank rates API.)
What is in it
One CSV per institution under rates/, five columns, identical everywhere:
| Column | Type | Meaning |
|---|---|---|
date | string | ISO 8601 publication date. Weekends and the publisher's holidays are simply absent. |
base | string | The currency being priced (ISO 4217). |
quote | string | The currency it is priced in. |
type | string | The publisher's own label: reference, middle, buy, sell, spot, indicative, monthly, monthly_average, quarterly, close, and a few more. |
value | float | How many quote one base buys. |
A few rows from rates/ecb.csv:
date,base,quote,type,value
2025-01-02,EUR,AUD,reference,1.6618
2025-01-02,EUR,BGN,reference,1.9558
2025-01-02,EUR,BRL,reference,6.42 Alongside the history there is latest/<code>.json with each institution's most recent table, and sources.json with the institution's name, country, home currency, kind (central bank or tax authority) and the URL of the page we collect from.
Coverage by depth, to give you a sense of what "back to 1914" actually means:
| Institution | Code | First year in dataset |
|---|---|---|
| Swiss National Bank | snb | 1914 |
| Bank of Korea | bok | 1964 |
| Bank of Israel | boi | 1975 |
| Hong Kong Monetary Authority | hkma | 1981 |
| Bank of Japan | boj | 1998 |
| European Central Bank | ecb | 1999 |
| Reserve Bank of India | rbi | 2003 |
| People's Bank of China | pboc | 2006 |
Most institutions only publish a few years of history on their own sites. Where a bank offered more, we took all of it.
Loading it
With the datasets library, the whole thing as one table:
from datasets import load_dataset
ds = load_dataset("AllRates/central-bank-exchange-rates", "rates") With pandas, one institution at a time. This is usually what you want, because 14.6 million rows is a lot of memory for a question about one bank:
import pandas as pd
ecb = pd.read_csv(
"hf://datasets/AllRates/central-bank-exchange-rates/rates/ecb.csv",
parse_dates=["date"],
)
usd = ecb[(ecb.base == "EUR") & (ecb.quote == "USD")].set_index("date")["value"]
print(usd.resample("YE").mean().tail()) With Polars, lazily, if you want to scan several banks without loading all of them:
import polars as pl
df = (
pl.scan_csv("hf://datasets/AllRates/central-bank-exchange-rates/rates/*.csv")
.filter((pl.col("base") == "USD") & (pl.col("quote") == "INR"))
.collect()
) With DuckDB, straight off the Hub, no download step:
INSTALL httpfs; LOAD httpfs;
SELECT date, value
FROM 'hf://datasets/AllRates/central-bank-exchange-rates/rates/rbi.csv'
WHERE base = 'USD' AND quote = 'INR' AND type = 'reference'
ORDER BY date DESC
LIMIT 5; Three things you can do with it that you could not do before
Compare what different central banks say the same pair is worth. The ECB publishes EUR→USD. The Fed publishes USD→EUR. The Bank of Canada publishes both against CAD. On any given day these disagree by a few basis points because they fix at different times. That spread is itself interesting, and until now you had to scrape three sites to see it.
Reconstruct a filing exactly. Pick the publisher your jurisdiction names, filter to type == "reference" (or whatever that publisher calls its headline rate), take the row for the invoice date. If the date is missing, the publisher did not publish that day, and your local rule will say whether to use the previous or next business day.
Train on a century of official fixings. The SNB series runs from 1914. The Bank of Korea from 1964. If you are building anything that needs long, clean, non-market FX series, this is a larger and more consistent corpus than we could find anywhere else in the open.
Conventions and caveats
We made a few decisions that you should know about before relying on the data.
- Direction follows the publisher. If the ECB says 1 EUR = 1.0850 USD, that is the row:
EUR,USD,reference,1.0850. We do not flip rates, and we do not compute crosses. If a bank does not publish GBP against JPY, there is no GBP/JPY row for that bank. typeis the publisher's label, normalised only in spelling. Abuyrate from one bank and abuyrate from another are not guaranteed to mean the same thing. Read the source page linked insources.jsonif it matters.- Gaps are real. A missing date means the institution did not publish, or published something our collector could not parse and our validation rejected. We would rather have a hole than a wrong number.
- Every row went through a cross-source plausibility check before it was committed. Values that disagree with the rest of the world by more than a threshold are quarantined and reviewed by hand. That caught a handful of unit errors during the backfill, which is why we trust the rest.
- Verify before you file. The figures are public information republished under CC BY 4.0. For anything legal or tax-related, the original publisher's page is the authority, and it is linked from
sources.jsonfor every institution. Our own data sources page lists the same links.
How it stays current
The dataset is a mirror of a GitHub repository, AllRates-Today/central-bank-exchange-rates, which is itself fed by the collection pipeline behind AllRatesToday. A scheduled Action runs four times a day, pulls each institution's latest table from a keyless open endpoint, commits only when something changed, and then pushes the consolidated per-institution CSVs to Hugging Face. So the Hub copy is never more than a few hours behind the source sites.
If you want the same data as small JSON files on a CDN instead of a 500 MB dataset, the GitHub repo serves data/latest.json and per-bank latest.json over jsDelivr with no key. If you want it as an API with a query language, the same rates are behind https://allratestoday.com/api/open/central-bank/<code>, also keyless, and the download page gives you any single table as CSV, Excel or JSON.
License
CC BY 4.0. Use it commercially, redistribute it, train on it. The one condition is visible credit to AllRatesToday with a link to allratestoday.com.
If you build something with it, open an issue on the GitHub repo or leave a note in the dataset's Community tab. Missing an institution you need? The repo README lists how new sources get added, and we are actively expanding coverage.
Official rates, as an API
The same 107 institutions, queryable by date, pair and publisher. Free tier, no credit card.
Explore the central bank rates API