Home Documentation Playground Pricing API Status Blog About FAQ Support

Best Free Historical Exchange Rate API (2026)

Reviewed by Madhushan, Fintech Developer — May 2026
Financial data analysis and reporting

Historical exchange rate data is essential for financial reporting, tax calculations, audit trails, and trend analysis. Accountants need last quarter's EUR/USD rate for revenue reconciliation. Analysts need five years of GBP/JPY data to model volatility. Fintech developers need to display the rate that applied when a transaction was processed, not today's rate.

The problem: most currency APIs lock historical data behind expensive paid plans. The few that offer it for free either limit you to a handful of ECB currencies, restrict query ranges to 30 days, or require a credit card to sign up. If you are building a financial application that needs to look up past rates reliably, the wrong API choice will cost you money or force a migration later.

This article compares the 6 best historical exchange rate APIs in 2026, with code examples in Python and cURL. Spoiler: AllRatesToday is the best option for free historical data, offering interbank-quality rates for 160+ currencies with no credit card required.

Side-by-Side Comparison

Here is how the top 6 historical exchange rate APIs compare:

API History Depth Free Tier Granularity Data Source Credit Card Required
AllRatesToday 20+ years Free tier, no CC Daily Reuters/Refinitiv No
Open Exchange Rates Since 1999 Paid only Daily Multiple aggregated Yes
Frankfurter Since 1999 Unlimited Daily (business days) ECB reference rates No
Fixer.io Since 1999 Paid only Daily ECB + market feeds Yes
ExchangeRate-API Limited Paid only Daily Multiple aggregated Yes
ECB (direct) Since 1999 Unlimited Daily (business days) ECB reference rates No

Key takeaway: AllRatesToday is the only API that provides free historical interbank rates for 160+ currencies sourced from Reuters/Refinitiv. Frankfurter and the ECB are free but limited to ~30 currencies with no weekend or holiday data. Every other API on this list locks historical data behind paid plans.

1. AllRatesToday — Best Overall for Historical Data

AllRatesToday provides historical exchange rate data on its free tier — a rarity among currency APIs. The data is sourced from Reuters/Refinitiv interbank feeds, covering 160+ currencies with daily granularity. You can query specific dates, date ranges, and time-series data without upgrading to a paid plan.

cURL: Fetch a historical rate for a specific date

curl -X GET "https://allratestoday.com/api/historical-rates?source=USD&target=EUR&date=2025-12-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "source": "USD",
  "target": "EUR",
  "date": "2025-12-31",
  "rate": 0.9234,
  "type": "mid-market"
}

cURL: Fetch a date range

curl -X GET "https://allratestoday.com/api/historical-rates?source=USD&target=EUR&from=2026-01-01&to=2026-03-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

Python: Historical rates with the SDK

from allratestoday import AllRatesToday

client = AllRatesToday("YOUR_API_KEY")

# Get historical rates for a specific period
history = client.get_historical_rates("USD", "EUR", "90d")

for point in history["rates"]:
    print(f"{point['time']}: 1 USD = {point['rate']} EUR")

Python: Historical rates with requests

import requests

API_KEY = "YOUR_API_KEY"

# Fetch USD/EUR rate for a specific date
response = requests.get(
    "https://allratestoday.com/api/historical-rates",
    params={
        "source": "USD",
        "target": "EUR",
        "date": "2025-06-15"
    },
    headers={"Authorization": f"Bearer {API_KEY}"}
)

data = response.json()
print(f"USD/EUR on {data['date']}: {data['rate']}")

Python: Build a historical rates DataFrame

import requests
import pandas as pd

API_KEY = "YOUR_API_KEY"

# Fetch an entire year of USD/EUR data
response = requests.get(
    "https://allratestoday.com/api/historical-rates",
    params={
        "source": "USD",
        "target": "EUR",
        "from": "2025-01-01",
        "to": "2025-12-31"
    },
    headers={"Authorization": f"Bearer {API_KEY}"}
)

data = response.json()

# Convert to pandas DataFrame
df = pd.DataFrame(data["rates"])
df["time"] = pd.to_datetime(df["time"])
df.set_index("time", inplace=True)

# Summary statistics for financial reporting
print(f"Period: {df.index.min().date()} to {df.index.max().date()}")
print(f"Average rate: {df['rate'].mean():.4f}")
print(f"Min rate:     {df['rate'].min():.4f}")
print(f"Max rate:     {df['rate'].max():.4f}")
print(f"Std dev:      {df['rate'].std():.4f}")
print(f"Data points:  {len(df)}")

# Export to CSV for accountants
df.to_csv("usd_eur_2025_rates.csv")

Tip for accountants: Export historical rates to CSV and import directly into Excel, QuickBooks, or your ERP system. The DataFrame output matches standard accounting date formats.

2. Open Exchange Rates

Open Exchange Rates has historical data going back to 1999, but it is locked behind paid plans. The free tier only provides the /latest.json endpoint. To access /historical/YYYY-MM-DD.json, you need at least the Unlimited plan.

# Requires paid plan
curl "https://openexchangerates.org/api/historical/2025-12-31.json?app_id=YOUR_APP_ID"
import requests

# Note: This requires a paid plan
APP_ID = "YOUR_APP_ID"

response = requests.get(
    "https://openexchangerates.org/api/historical/2025-12-31.json",
    params={"app_id": APP_ID, "base": "USD"}
)

data = response.json()
print(f"USD/EUR on 2025-12-31: {data['rates']['EUR']}")

3. Frankfurter

Frankfurter is a free, open-source API that wraps European Central Bank (ECB) reference rates. It is the best free option if you only need the ~30 currencies the ECB publishes. However, ECB data only covers business days (no weekends or holidays) and updates once per day at 16:00 CET.

# Fetch a specific historical date
curl "https://api.frankfurter.app/2025-12-31?from=USD&to=EUR,GBP,JPY"
# Fetch a date range
curl "https://api.frankfurter.app/2025-01-01..2025-12-31?from=USD&to=EUR"
import requests

# No API key needed
response = requests.get(
    "https://api.frankfurter.app/2025-01-01..2025-03-31",
    params={"from": "USD", "to": "EUR"}
)

data = response.json()

for date, rates in sorted(data["rates"].items()):
    print(f"{date}: 1 USD = {rates['EUR']} EUR")

When Frankfurter works: If you only need EUR, USD, GBP, JPY, CHF, and a handful of other major currencies with no weekend data, Frankfurter is a solid free option. For anything beyond ECB coverage, you need AllRatesToday.

4. Fixer.io

Fixer has historical data going back to 1999, but like Open Exchange Rates, it requires a paid plan to access it. The free tier is restricted to 100 requests per month with the /latest endpoint only, EUR base, and HTTP (not HTTPS).

# Requires paid plan
curl "http://data.fixer.io/api/2025-12-31?access_key=YOUR_KEY&base=EUR&symbols=USD,GBP"
import requests

# Note: Historical endpoint requires paid plan
# Free tier: HTTP only, EUR base only
API_KEY = "YOUR_API_KEY"

response = requests.get(
    "http://data.fixer.io/api/2025-12-31",
    params={
        "access_key": API_KEY,
        "base": "EUR",
        "symbols": "USD,GBP,JPY"
    }
)

data = response.json()
if data.get("success"):
    print(f"EUR/USD on 2025-12-31: {data['rates']['USD']}")

5. ExchangeRate-API

ExchangeRate-API offers historical data on paid plans only. The free tier provides 1,500 requests per month but is restricted to the /latest/ endpoint. There is no Python SDK or time-series endpoint.

# Requires paid plan (pair-specific historical lookup)
curl "https://v6.exchangerate-api.com/v6/YOUR_KEY/history/USD/2025/12/31"
import requests

# Note: Historical endpoint requires paid plan
API_KEY = "YOUR_API_KEY"

response = requests.get(
    f"https://v6.exchangerate-api.com/v6/{API_KEY}/history/USD/2025/12/31"
)

data = response.json()
if data.get("result") == "success":
    print(f"USD/EUR on 2025-12-31: {data['conversion_rates']['EUR']}")

6. ECB (European Central Bank) — Direct Access

You can access the ECB's Statistical Data Warehouse directly via their SDMX API. This is the raw source that Frankfurter wraps. It is free and requires no API key, but the SDMX/XML response format is cumbersome compared to JSON APIs.

# Fetch daily USD/EUR from ECB's SDMX API (XML response)
curl "https://data-api.ecb.europa.eu/service/data/EXR/D.USD.EUR.SP00.A?startPeriod=2025-01-01&endPeriod=2025-12-31"
import requests
import xml.etree.ElementTree as ET

# Fetch USD/EUR from ECB SDMX API
response = requests.get(
    "https://data-api.ecb.europa.eu/service/data/EXR/D.USD.EUR.SP00.A",
    params={
        "startPeriod": "2025-01-01",
        "endPeriod": "2025-03-31"
    },
    headers={"Accept": "application/xml"}
)

# Parse XML response (SDMX format)
root = ET.fromstring(response.text)
ns = {
    "generic": "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic"
}

for obs in root.findall(".//generic:Obs", ns):
    date = obs.find("generic:ObsDimension", ns).get("value")
    rate = obs.find("generic:ObsValue", ns).get("value")
    print(f"{date}: 1 USD = {rate} EUR")

Use Case: Monthly Revenue Reconciliation

Accountants frequently need the exchange rate on the exact date a transaction was invoiced. Here is how to build a reconciliation script with AllRatesToday:

import requests
import pandas as pd
from datetime import datetime

API_KEY = "YOUR_API_KEY"

# Sample invoice data (date, amount, currency)
invoices = [
    {"date": "2026-01-15", "amount": 5000, "currency": "EUR"},
    {"date": "2026-02-03", "amount": 12000, "currency": "GBP"},
    {"date": "2026-02-20", "amount": 800000, "currency": "JPY"},
    {"date": "2026-03-10", "amount": 25000, "currency": "CAD"},
    {"date": "2026-04-01", "amount": 150000, "currency": "INR"},
]

base_currency = "USD"
results = []

for inv in invoices:
    response = requests.get(
        "https://allratestoday.com/api/historical-rates",
        params={
            "source": inv["currency"],
            "target": base_currency,
            "date": inv["date"]
        },
        headers={"Authorization": f"Bearer {API_KEY}"}
    )

    data = response.json()
    rate = data["rate"]
    usd_amount = round(inv["amount"] * rate, 2)

    results.append({
        "Invoice Date": inv["date"],
        "Original Amount": inv["amount"],
        "Currency": inv["currency"],
        "Exchange Rate": rate,
        "Amount (" + base_currency + ")": usd_amount
    })

# Create accounting report
df = pd.DataFrame(results)
print(df.to_string(index=False))
col = "Amount (" + base_currency + ")"
total = df[col].sum()
print("\nTotal Revenue (USD): $" + format(total, ",.2f"))

# Export for accounting software
df.to_csv("revenue_reconciliation_q1_2026.csv", index=False)
print("\nExported to revenue_reconciliation_q1_2026.csv")

Use Case: Currency Volatility Analysis

Analysts tracking exchange rate trends can use AllRatesToday's historical data to compute rolling averages and standard deviations:

import requests
import pandas as pd

API_KEY = "YOUR_API_KEY"

# Fetch 1 year of GBP/USD data
response = requests.get(
    "https://allratestoday.com/api/historical-rates",
    params={
        "source": "GBP",
        "target": "USD",
        "from": "2025-05-01",
        "to": "2026-05-01"
    },
    headers={"Authorization": f"Bearer {API_KEY}"}
)

data = response.json()
df = pd.DataFrame(data["rates"])
df["time"] = pd.to_datetime(df["time"])
df.set_index("time", inplace=True)

# Calculate rolling statistics
df["ma_30"] = df["rate"].rolling(window=30).mean()
df["ma_90"] = df["rate"].rolling(window=90).mean()
df["volatility_30d"] = df["rate"].rolling(window=30).std()

# Monthly summary
monthly = df["rate"].resample("M").agg(["mean", "min", "max", "std"])
monthly.columns = ["Avg Rate", "Min", "Max", "Volatility"]
print("\nMonthly GBP/USD Summary:")
print(monthly.to_string())

# Identify highest volatility months
most_volatile = monthly["Volatility"].idxmax()
print(f"\nMost volatile month: {most_volatile.strftime('%B %Y')}")
print(f"Volatility (std): {monthly.loc[most_volatile, 'Volatility']:.4f}")

cURL Quick Reference

For analysts and developers who prefer command-line access:

# Specific date
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://allratestoday.com/api/historical-rates?source=USD&target=EUR&date=2025-12-31"

# Date range
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://allratestoday.com/api/historical-rates?source=USD&target=EUR&from=2025-01-01&to=2025-12-31"

# Multiple targets
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://allratestoday.com/api/historical-rates?source=USD&target=EUR,GBP,JPY&date=2025-12-31"

# Pipe to jq for clean output
curl -s -H "Authorization: Bearer YOUR_API_KEY" \
  "https://allratestoday.com/api/historical-rates?source=USD&target=EUR&from=2026-01-01&to=2026-01-31" \
  | jq '.rates[] | "\(.time): \(.rate)"'

Why AllRatesToday Wins for Historical Data

After comparing all six options, here is why AllRatesToday is the best choice for historical exchange rates:

Quick Reference

Access Free Historical Exchange Rate Data

Get your free API key in 30 seconds. Query 20+ years of interbank mid-market rates for 160+ currencies with no credit card required. Compare all options on our Exchange Rate API page.

Get Your Free API Key