FreeAPI.watch

Frankfurter API for Currency Conversion in Python

Languages: Python, curl  ·  Estimated time: ~5 minutes  ·  Frankfurter live status ↗

Frankfurter serves European Central Bank exchange rates with no key, no rate limit, and a clean REST interface. This tutorial covers fetching latest rates, converting amounts, and querying historical rates.

Frankfurter is the best free currency API for most use cases: no key, no credit card, unlimited calls, ECB data. It's been running reliably for years. The live status is at /frankfurter.

The ECB only publishes rates on business days. On weekends and EU bank holidays, Frankfurter returns the most recent available rate — this is the correct behaviour, not a bug.

Basic Requests with curl

# Latest rates (all currencies vs EUR base)
curl https://api.frankfurter.app/latest

# Latest rates vs USD base
curl "https://api.frankfurter.app/latest?from=USD"

# Convert 100 USD to GBP and EUR
curl "https://api.frankfurter.app/latest?amount=100&from=USD&to=GBP,EUR"
# { "amount": 100.0, "base": "USD", "date": "2026-05-14",
#   "rates": { "GBP": 79.23, "EUR": 91.88 } }

# Historical rate on a specific date
curl "https://api.frankfurter.app/2024-01-15?from=USD&to=EUR"

# All supported currencies
curl https://api.frankfurter.app/currencies

Python: Currency Conversion Class

import requests
from datetime import date, timedelta
from functools import lru_cache

FRANKFURTER_BASE = "https://api.frankfurter.app"

@lru_cache(maxsize=100)
def get_rates(base: str = "EUR", on_date: str = "latest") -> dict:
    """
    Fetch exchange rates. Results are cached (rates change once per day at most).
    """
    url = f"{FRANKFURTER_BASE}/{on_date}"
    resp = requests.get(url, params={"from": base}, timeout=10)
    resp.raise_for_status()
    return resp.json()

def convert(amount: float, from_ccy: str, to_ccy: str) -> float:
    """
    Convert an amount from one currency to another.
    Uses today's ECB rate (updated once per business day).
    """
    if from_ccy == to_ccy:
        return amount

    data = get_rates(base=from_ccy)
    rate = data["rates"].get(to_ccy)
    if rate is None:
        raise ValueError(f"Currency '{to_ccy}' not supported. Check /currencies endpoint.")
    return round(amount * rate, 2)

def get_historical_rate(from_ccy: str, to_ccy: str, on_date: date) -> float:
    """
    Get the ECB rate for a specific date.
    Note: weekends/holidays return the most recent business day's rate.
    """
    data = get_rates(base=from_ccy, on_date=on_date.isoformat())
    return data["rates"][to_ccy]

# Examples
print(convert(100, "USD", "EUR"))   # e.g. 91.88
print(convert(500, "GBP", "JPY"))   # e.g. 94210.50

# Historical: what was 1 USD in EUR on 2024-01-01?
rate = get_historical_rate("USD", "EUR", date(2024, 1, 2))  # Jan 1 is a holiday
print(f"1 USD = {rate} EUR on 2024-01-02")

Handling the ECB Calendar

The ECB doesn't publish rates on Saturdays, Sundays, or EU bank holidays (New Year's, Easter, Christmas). Calling Frankfurter for `2026-01-01` returns rates dated `2025-12-31` (the last business day). The `date` field in the response tells you which date the rates are from.

If your application displays the rate date to users (e.g. 'rate as of [date]'), use `data['date']` rather than the date you requested.

# Getting the actual rate date vs the requested date
resp = requests.get("https://api.frankfurter.app/2026-01-01?from=USD&to=EUR")
data = resp.json()
print(f"Requested: 2026-01-01, Rate date: {data['date']}")
# Rate date will be 2025-12-31 (New Year's Day is a holiday)

Frequently Asked Questions

How accurate are Frankfurter's rates?

They're the ECB reference rates — accurate as of 16:00 CET each business day. These are not live market rates; they're published once daily by the European Central Bank. For high-frequency trading or intraday FX rates, you need a dedicated financial data API. For general currency conversion (e-commerce checkout, invoicing, display), ECB rates are sufficient and authoritative.

Can I use Frankfurter commercially?

Yes. Frankfurter is open-source and the underlying ECB data is public. There are no restrictions on commercial use. The rate limit is generous (effectively unlimited for reasonable use). If you're building something that will generate thousands of requests per minute, consider caching aggressively or using the ECB XML endpoint directly.

What currencies does Frankfurter support?

Approximately 30 currencies: USD, GBP, JPY, CHF, CAD, AUD, CNY, HKD, and other major currencies tracked by the ECB. It does not cover exotic currencies or cryptocurrencies. For those, look at Open Exchange Rates (1,000 calls/month, free key) or ExchangeRate-API (1,500 calls/month, free key).

← All tutorials  ·  Check Frankfurter live status →