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

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: https://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 repo License CC BY 4.0 Size 515 MB CSV, auto-converted to Parquet 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. 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. 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; 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. 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. type is the publisher's label, normalised only in spelling. A buy rate from one bank and a buy rate from another are not guaranteed to mean the same thing. Read the source page linked in sources.json if 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.json for every institution. 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. CC BY 4.0. Use it commercially, redistribute it, train on it. The one condition is visible credit to AllRatesToday with a link to https://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.
Key Takeaways
- •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
- •This story was reported by Dev.to, covering developments in the dev space.
- •AI advancements continue to reshape industries — read the full article on Dev.to for complete coverage.
📖 Continue reading the full article:
Read Full Article on Dev.to →


