Python
You don't need an SDK. Frankfurter is plain HTTPS returning flat JSON, so the HTTP library you already use is all it takes. No API key, no account.
Quick Start
Fetch a rate and convert:
import requests
res = requests.get("https://api.frankfurter.dev/v2/rate/USD/EUR")
data = res.json()
total = 100 * data["rate"] The response is one flat object: {"date": "2026-07-17", "base": "USD", "quote": "EUR", "rate": 0.8739}. An invalid currency code returns a 422 with a JSON message.
Money-Safe Math
Floats are fine for display and wrong for accounting. Parse rates as Decimal by passing parse_float through to json.loads:
from decimal import Decimal
import requests
res = requests.get("https://api.frankfurter.dev/v2/rate/USD/EUR")
data = res.json(parse_float=Decimal)
total = Decimal("100") * data["rate"]Several Quotes at Once
/v2/rates returns a flat array, one row per quote. Reshape it into a lookup with a comprehension. Omit quotes to get every currency.
res = requests.get(
"https://api.frankfurter.dev/v2/rates",
params={"base": "USD", "quotes": "EUR,GBP,JPY"},
)
rates = {row["quote"]: row["rate"] for row in res.json()}Cache What Doesn't Change
Historical rates are immutable, so cache them forever. Latest rates change only as providers publish, at most a few times a working day, so a short TTL is plenty. With requests-cache, caching is one line:
import requests_cache
session = requests_cache.CachedSession("frankfurter", expire_after=3600)
res = session.get("https://api.frankfurter.dev/v2/rate/USD/EUR")
data = res.json()Async
In an async web app, use httpx:
import httpx
API = "https://api.frankfurter.dev"
async def rate(base, quote):
async with httpx.AsyncClient(base_url=API) as client:
res = await client.get(f"/v2/rate/{base}/{quote}")
res.raise_for_status()
return res.json()["rate"]Pin an Official Provider
By default you get the blend across every provider. Add providers to pin one named authority, which matters when a jurisdiction requires its central bank's published rate, like the ECB reference rate for EU VAT.
res = requests.get(
"https://api.frankfurter.dev/v2/rate/USD/EUR",
params={"providers": "ECB"},
)
data = res.json()Browse all providers. A pinned provider follows its own publishing schedule, so its latest rate can trail the blended latest by a day.
Analyzing a Series?
For date ranges, monthly grouping, and charts, the CSV endpoint loads straight into a DataFrame. See pandas.