Tillbaka till bloggen

Frankfurter API Alternatives in 2026: An Honest Developer Comparison

V
Vlado Grigirov
August 28, 2026
Currency API Exchange Rates Frankfurter API Comparison Free API Developer Guide

If you are searching for Frankfurter API alternatives, you have probably already shipped something with it. Frankfurter is the default free exchange rates API for a generation of side projects: no API key, no signup, no quota, a clean REST surface, and an open-source codebase you can read in an afternoon. Then something changes — a compliance question, a weekend bug, a customer who needs intraday rates — and you start wondering what else is out there.

This guide is an honest look at that decision. It covers what Frankfurter actually does in 2026 (most comparison articles are working from outdated information), the five specific limits that push teams off it, and the three realistic paths forward: self-host it, move to a keyed commercial API, or run a hybrid. There is a migration example at the end.

What Frankfurter actually is in 2026

Almost every "best free currency API" roundup still describes Frankfurter as "ECB rates, about 30 currencies, no weekend data." That was true for years. It is no longer accurate.

The v2 API on frankfurter.dev tracks daily rates from 84 central banks, covering 201 currencies, with history reaching back to 1948. It is genuinely free for commercial use, requires no authentication, publishes no monthly or daily quota, and ships CSV and NDJSON output alongside JSON. There is an OpenAPI spec, an llms.txt, and an MCP server for agent workflows. You can self-host it with Docker.

That is a stronger product than the roundups give it credit for, and it is worth saying plainly: for a large number of projects, Frankfurter is the correct answer and you should not migrate. If you are building a personal finance tracker, a currency reference page, an invoicing tool that books rates once a day, or a data-science notebook pulling a decade of monthly averages, Frankfurter does that well and costs nothing.

The rest of this article is about the cases where it does not.

Five limits that send teams looking for Frankfurter API alternatives

1. Daily reference rates are not live rates

This is the structural one, and it is not a bug — it is what the data source is. Central bank reference rates are published once per business day. The ECB, for example, publishes its euro reference rates each working day at around 16:00 CET. Frankfurter faithfully exposes those numbers.

That means a rate you fetch at 09:00 and a rate you fetch at 15:00 are the same number, even if the market moved 1.2% in between. For a display-only converter, nobody notices. For a checkout page, a payout calculation, or anything where a customer is comparing your number against Google, that lag becomes a support ticket.

If your product needs rates that move during the day, you need a market-data source rather than a reference-rate source. The Finexly API refreshes every minute during market hours across 170+ currencies, which is a different data model, not a better version of the same one. Our post on where exchange rate APIs get their data walks through the distinction in more detail.

2. There are no weekend or holiday rows

Central banks do not publish on Saturdays, Sundays, or national holidays. So a query for 2026-08-23 returns nothing useful, and a time series over a month has roughly 21 rows, not 31.

Every team hits this the same way: a nightly job runs on Sunday, gets an empty or shifted result, and either crashes or — much worse — silently writes a null into a ledger. If you are building on daily rates from any central bank source, you need an explicit forward-fill policy and you need to write it down, because "use the last published rate" and "skip the row" produce different financial statements.

3. Blended rates can shift after publication

By default, Frankfurter blends rates across all contributing providers. Its own FAQ is refreshingly direct about the consequence: the last decimal places may shift as new data comes in, and for compliance you should filter to a specific provider.

That is a completely reasonable design for general use. It is a problem if you store a rate, show it to a user, and later reconcile against a re-fetch — you will find small discrepancies that are very hard to explain to an auditor. The fix on Frankfurter is to pass providers=ECB (or whichever authority governs you) rather than accepting the blend. The fix in your own system is to persist the rate you actually used at transaction time and never re-derive it. That rule applies to every provider, and we cover it in the exchange rates and tax reporting guide.

4. No API key means no quota — and no visibility

"No API key required" is Frankfurter's best feature and its most underrated risk. Because there is no key:

  • You have no per-application quota. You share a public rate limiter with everyone else on the internet, including whoever is hammering it from a badly written loop right now.
  • You have no usage telemetry. There is no dashboard telling you that your call volume tripled last Tuesday.
  • You have no support relationship. There is a status page and a GitHub issue tracker, which is more than many free services offer, but no SLA and no one to page.

The project's own guidance is explicit: for high-volume use, cache responses, self-host, or query the datasets directly. That is honest advice, and it is also the moment a lot of teams start evaluating Frankfurter API alternatives — not because the data is wrong, but because they have taken on a production dependency with no contract behind it.

5. There is no conversion endpoint

Frankfurter documents this deliberately: fetch the rate and multiply. It is three lines of code.

It is also three lines of code that get written slightly differently in six places in your codebase, one of which divides when it should multiply. A dedicated convert endpoint is not a technical necessity; it is a way of having exactly one implementation of the rounding and direction rules. If you have ever shipped a bug where EUR→USD and USD→EUR disagreed by 0.3%, you know why this matters. Our guide to currency rounding and decimal places covers the rest of that minefield.

Frankfurter API alternatives compared

Free-tier details below are as published by each provider at the time of writing. Verify them before you commit — free tiers change more often than documentation does.

APIFree tierUpdate frequencyAuthBase currencyBest for
FrankfurterUnlimited (rate-limited, no SLA)Daily, business daysNoneAnyHobby projects, accounting, historical research
Self-hosted FrankfurterFree + your infra costDaily, business daysYoursAnyTeams that need control and already run Docker
Finexly1,000 req/moEvery minute during market hoursBearer keyAny (custom base on higher plans)Products that need intraday rates plus a support path
ExchangeRate-API~1,500 req/moDailyKeyAnyOnce-a-day dashboards
Open Exchange Rates1,000 req/moHourlyKeyUSD only on freeServer-side apps that can live with a USD base
Fixer.io100 req/moHourlyKeyEUR only on freeLegacy integrations
Two things stand out. First, nobody beats Frankfurter on quota, because Frankfurter does not have one. If raw call volume against daily rates is your constraint, the answer is to self-host Frankfurter, not to buy a smaller quota somewhere else. Second, the paid options are not selling you the same data with a nicer wrapper — they are selling a different update frequency and a support relationship. If neither of those is your problem, moving is a downgrade.

We have side-by-side breakdowns of several of these in the currency API comparison and in our real-time exchange rates API comparison.

Option 1: self-host Frankfurter

The most underused answer. Frankfurter publishes a Docker image, and running it yourself removes the two things that actually worry production teams: the shared rate limiter and the lack of control.

docker run -d -p 8080:8080 --name frankfurter \
  lineofflight/frankfurter

What you get: unlimited internal calls, your own uptime destiny, and the ability to pin a provider. What you take on: a container, a database, monitoring, and someone who notices when the upstream scrape breaks on a bank holiday. That is a real cost — it is roughly the argument in our build vs. buy analysis, applied to a codebase someone else already wrote.

Self-hosting is the right call when your volume is high, your latency requirements are strict, and daily reference rates are genuinely sufficient. It does not help at all if the problem is that you need intraday rates — you will just be running your own copy of the same daily data.

Option 2: move to a keyed API with intraday rates

If the reason you are here is rate freshness, coverage of exotic pairs, or the need for someone to answer an email, a keyed commercial API is the honest answer.

Here is the same task on both. Frankfurter first:

curl "https://api.frankfurter.dev/v2/rate/USD/EUR"

And the Finexly equivalent:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.finexly.com/v1/rate?from=USD&to=EUR"
{ "pair": "USD_EUR", "rate": 0.9215 }

Endpoint mapping

TaskFrankfurter v2Finexly v1
List currenciesGET /v2/currenciesGET /v1/currencies
Single pairGET /v2/rate/EUR/USDGET /v1/rate?from=EUR&to=USD
Many pairsGET /v2/rates?base=USD&quotes=EUR,GBPGET /v1/convert?q=USD_EUR,USD_GBP
Convert an amount(none — multiply yourself)GET /v1/convert-amount?from=USD&to=EUR&amount=100
HistoricalGET /v2/rates?date=1999-01-04Paid plans — see the historical rates guide
The main shape change is the multi-pair call. Frankfurter uses one base and a list of quotes; Finexly takes a list of explicit BASE_QUOTE pairs, which lets you fetch USD_EUR and GBP_JPY in the same request without a cross-divide.

A migration wrapper

Do not sprinkle a new client through your codebase. Put both behind one interface, so switching back is a config change:

import os
import requests

FINEXLY_KEY = os.environ["FINEXLY_API_KEY"]

def get_rate(base: str, quote: str, provider: str = "finexly") -> float:
    """Return the mid-market rate for base->quote."""
    if provider == "frankfurter":
        r = requests.get(
            f"https://api.frankfurter.dev/v2/rate/{base}/{quote}",
            timeout=5,
        )
        r.raise_for_status()
        return float(r.json()["rate"])

    r = requests.get(
        "https://api.finexly.com/v1/rate",
        params={"from": base, "to": quote},
        headers={"Authorization": f"Bearer {FINEXLY_KEY}"},
        timeout=5,
    )
    r.raise_for_status()
    return float(r.json()["rate"])

print(get_rate("USD", "EUR"))

Two details worth copying. The API key comes from the environment, never from source — the Finexly docs note that keys passed as query parameters can leak through server access logs and HTTP referrer headers, so the Authorization header is the production path. And every call has a timeout, because the default in most HTTP clients is "wait forever."

Watch the X-RateLimit-Limit, X-RateLimit-Used and X-RateLimit-Units response headers to see quota consumption in real time. That telemetry is the thing you cannot get from an unauthenticated API, and it is often the actual reason teams move.

Option 3: the hybrid — cache one, fall back to the other

The pattern most production systems converge on. Use your primary provider, cache aggressively, and keep the free unauthenticated API as a last-resort fallback:

const CACHE = new Map();
const TTL_MS = 60_000;

async function getRate(base, quote) {
  const key = `${base}_${quote}`;
  const hit = CACHE.get(key);
  if (hit && Date.now() - hit.at < TTL_MS) return hit.rate;

  let rate;
  try {
    const res = await fetch(
      `https://api.finexly.com/v1/rate?from=${base}&to=${quote}`,
      { headers: { Authorization: `Bearer ${process.env.FINEXLY_API_KEY}` } }
    );
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    rate = (await res.json()).rate;
  } catch (err) {
    // Degrade to daily reference rates rather than failing the request
    const res = await fetch(`https://api.frankfurter.dev/v2/rate/${base}/${quote}`);
    rate = (await res.json()).rate;
  }

  CACHE.set(key, { rate, at: Date.now() });
  return rate;
}

A 60-second cache on a 1,000-request free plan comfortably covers a small app, because your call volume becomes a function of time, not traffic. Flag fallback responses in your logs so a silent degradation to yesterday's rate does not go unnoticed for a week. There is more on TTL choice and retry behaviour in our caching and error handling guide.

A decision checklist

Work down this list and stop at the first "yes":

  1. Do rates need to change during the trading day? → You need a market-data API, not a reference-rate API.
  2. Is this a regulated or audited flow? → Pin one named provider, store the rate you used, never re-derive.
  3. Is your call volume high but daily rates fine? → Self-host Frankfurter.
  4. Do you need someone to answer when it breaks? → You need a keyed plan with a support tier.
  5. None of the above? → Stay on Frankfurter. Cache it, handle the weekend gap, and spend your time elsewhere.

Most teams that go looking for Frankfurter API alternatives discover at step 5 that their real problem was a missing cache and an unhandled Sunday.

Frequently Asked Questions

Is the Frankfurter API really free for commercial use? Yes. The project states it is free for commercial use, with no monthly or daily quota — requests are rate-limited only to prevent abuse. The trade-off is that there is no SLA and no support contract, so the risk sits with you.

Does Frankfurter only provide ECB rates? Not anymore. The v2 API blends data from 84 central banks across 201 currencies, and you can scope to a single source with the providers parameter. The widespread "ECB only, 30 currencies" description refers to older versions.

Why does Frankfurter return no data for weekends? Because central banks do not publish reference rates on non-business days. Any API built on central bank data has the same gap. You either forward-fill the last published rate or use a market-data source that quotes continuously.

What is the best free alternative to Frankfurter? It depends on what "free" needs to buy you. For unlimited daily rates, nothing beats Frankfurter — self-host it. For a free tier with intraday updates and a real API key, Finexly's free plan gives you 1,000 requests a month across 170+ currencies. See our free currency API guide for the wider landscape.

Can I use Frankfurter and a paid API together? Yes, and it is a sensible architecture. Route normal traffic to your primary provider and fall back to Frankfurter on error, as in the hybrid example above. Just make sure fallback responses are logged, since they carry different freshness guarantees.

Get started

If daily reference rates are enough for what you are building, stay on Frankfurter — it is a good project and it will not cost you anything. If you need rates that move during the day, coverage beyond central bank reference sets, or usage headers you can actually monitor, get your free Finexly API key — no credit card required. Start with 1,000 requests per month across 170+ currencies and upgrade as you grow, or compare the tiers on our pricing page first.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →

Dela den här artikeln