Best Free Currency Exchange Rate APIs

Blog / Web Development · February 19, 2022 · Updated June 10, 2026 · 7 min read
Best Free Currency Exchange Rate APIs

A currency exchange rate API is a web service that returns up-to-date conversion rates between currencies — for example, how many Indian rupees one US dollar buys — as machine-readable JSON your app can call on demand. In 2026 there are still several genuinely free options. The trick is knowing which are free without a credit card, which need an API key, and which have quietly tightened their free tiers.

The short answer: for most apps, Frankfurter is the easiest free choice. It serves European Central Bank (ECB) reference rates as JSON, needs no API key, and is open source. If you need more currencies or a different base currency, ExchangeRate-API's open endpoint and Open Exchange Rates' free tier are good no-cost backups. Below is an honest 2026 comparison, working code, and the caching pattern you should use in production.

Full disclosure: years ago we ran a small free rates API of our own (ratesapi.io). It has since been retired, and these days we simply point teams to the maintained options below.

Free currency exchange rate APIs compared

API Key required? Free-tier limit* Base currency Historical data Data source / licensing
Frankfurter No No hard cap (fair use) Any (cross-rates) Yes, daily since 1999 ECB euro reference rates; open source
ExchangeRate-API (open endpoint) No Once-daily, fair use Per-request base Limited Aggregated; attribution requested
ExchangeRate-API (free key) Yes ~1,500 requests/mo Any No (paid) Aggregated central-bank + market
Open Exchange Rates Yes ~1,000 requests/mo USD only on free Limited (paid) Aggregated; hourly updates
exchangerate.host Yes (now) ~100 requests/mo Any Yes APILayer; 170+ fiat + crypto
Fixer Yes ~100 requests/mo, HTTP only EUR only on free Limited (paid) APILayer; ~170 currencies
ECB reference rates (raw XML/CSV) No None (static file) EUR Yes (90-day + full-history files) ECB; free to reuse

*Free-tier limits and base-currency restrictions change often — these figures are indicative as of 2026. Always confirm on the provider's current pricing/docs page before you build. Note that ECB-based feeds (Frankfurter, the raw ECB files) publish once per working day around 16:00 CET, carry no intraday or weekend updates, and cover fiat currencies only — no crypto.

How to choose a currency API

Run any candidate past these criteria before you wire it into a product:

  • Accuracy and data source. ECB reference rates (Frankfurter, the ECB feed) are reliable daily midpoints — great for invoicing, reporting and price display. They are not live trading rates with bid/ask spreads.
  • Update frequency. Daily is fine for most apps. If you need hourly or intraday rates, you are usually into paid territory.
  • Rate limits and quota. A 100-requests-per-month cap disappears fast. Estimate real traffic, then add caching (below) so usage stays flat regardless of pageviews.
  • Base-currency flexibility. Some free tiers lock you to USD or EUR. You can still convert via cross-rates, but a flexible base is simpler.
  • Historical / time-series data. Needed for charts, back-dated invoices or audit trails. Free on Frankfurter and the ECB feed; often paid elsewhere.
  • Reliability and SLA. Free tiers rarely promise uptime. For anything revenue-critical, plan a fallback provider or a cached last-good value.
  • Licensing and attribution. Check whether commercial use is allowed and whether attribution is required. ECB data is free to reuse; some aggregators ask for a credit link.

Worked example: convert currency with Frankfurter (no key)

Frankfurter is the quickest way to get a working conversion with zero sign-up. You hit its latest-rates endpoint with a base currency, the target symbol(s), and optionally an amount it will pre-multiply for you. Here it is in modern JavaScript with fetch:

/**
 * Convert an amount between currencies using Frankfurter (no API key).
 * Frankfurter serves daily ECB reference rates as JSON.
 * @param {number} amount - Amount in the base currency to convert.
 * @param {string} from - ISO 4217 base code, e.g. "USD".
 * @param {string} to - ISO 4217 target code, e.g. "INR".
 * @returns {Promise<number>} The converted amount in the target currency.
 */
async function convert(amount, from, to) {
  const url = `https://api.frankfurter.dev/v1/latest?base=${from}&symbols=${to}&amount=${amount}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`Frankfurter error: ${res.status}`);
  const data = await res.json();
  return data.rates[to];
}

// 100 USD -> INR
const inr = await convert(100, 'USD', 'INR');
console.log(`100 USD = ${inr} INR`);

The same call in Python uses the requests library:

import requests

def convert(amount, base, target):
    """Convert an amount between currencies using Frankfurter (no API key).

    Frankfurter serves daily ECB reference rates as JSON.
    Returns the converted amount as a float.
    """
    url = "https://api.frankfurter.dev/v1/latest"
    params = {"base": base, "symbols": target, "amount": amount}
    resp = requests.get(url, params=params, timeout=10)
    resp.raise_for_status()
    return resp.json()["rates"][target]

print(convert(100, "USD", "INR"))  # e.g. 9535.0

Cache rates in production (don't hammer the API)

Because ECB-based rates only change once per working day, fetching them on every request is wasteful and will burn through any free-tier quota for no benefit — you get the same numbers back. Fetch once, cache the result, and serve it to all users:

  • Cache in Redis or your database with a 12-24 hour TTL.
  • Keep the last good response and serve it if a refresh fails, so a provider outage never breaks checkout.
  • Remember there is no new rate on weekends or ECB holidays — your cache simply holds the last published value, which is correct.
  • For higher volume or multiple providers, put a thin proxy/service in front so you can swap sources and add monitoring without touching every app.

A minimal in-memory cache looks like this:

/**
 * Cache ECB-based rates for the day. ECB publishes once per working day
 * (~16:00 CET), so refetching more often wastes quota and changes nothing.
 */
const TTL_MS = 12 * 60 * 60 * 1000; // 12 hours
let cache = { at: 0, base: '', rates: null };

/**
 * @param {string} [base] - Base currency code (default "EUR").
 * @returns {Promise<Record<string, number>>} Map of currency code -> rate.
 */
async function getRates(base = 'EUR') {
  const fresh = cache.rates && cache.base === base && Date.now() - cache.at < TTL_MS;
  if (fresh) return cache.rates;

  const res = await fetch(`https://api.frankfurter.dev/v1/latest?base=${base}`);
  if (!res.ok) {
    if (cache.rates) return cache.rates; // serve last-good on failure
    throw new Error(`rates fetch failed: ${res.status}`);
  }
  const data = await res.json();
  cache = { at: Date.now(), base, rates: data.rates };
  return cache.rates;
}

When a paid commercial tier is worth it

Free tiers are perfect for prototypes, dashboards, invoicing and price display. Move to a paid plan when you genuinely need: high request volume that exceeds free quotas, an uptime SLA for revenue-critical flows, intraday or real-time rates with bid/ask spreads (for trading or FX-sensitive pricing), guaranteed historical / time-series access, or commercial licensing that the free terms don't cover. Even then, keep the caching layer above — it protects you from rate limits and outages on any tier.

At MicroPyramid we've spent 12+ years and 50+ projects integrating third-party APIs — currency, payments, mapping, AI — into web applications and products. Whether you want a clean Python service that normalises rates from several providers with a fallback, or a full custom application where pricing and reporting depend on accurate FX, we build it to be reliable and easy to maintain. If you need a lightweight rates proxy or microservice, a FastAPI backend is a common, fast-to-ship choice.

Frequently Asked Questions

Which currency exchange rate API is completely free with no API key?

Frankfurter is the main one: it serves ECB euro reference rates as JSON, needs no key or sign-up, and is open source. ExchangeRate-API also offers an open endpoint that works without a key for once-daily rates. The raw ECB XML/CSV feed is free too, if you don't mind parsing it yourself.

How accurate are free exchange rate APIs?

Free APIs based on ECB reference rates (Frankfurter, the ECB feed) give accurate daily midpoint rates — fine for invoicing, reporting, price display and general conversion. They are not live trading rates: there is no bid/ask spread, no intraday movement, and ECB does not publish on weekends or its holidays. For real-time FX dealing you need a paid market-data feed.

Can I get historical exchange rates for free?

Yes. Frankfurter exposes historical daily rates back to 1999 — request a specific date or a date range — and the ECB publishes 90-day and full-history files. Some commercial APIs gate historical and time-series data behind paid plans, so confirm availability before you rely on it.

Why did exchangerate.host and fixer.io stop being free without a key?

Both moved under the APILayer umbrella and now require a free API key with monthly request caps, whereas older versions allowed keyless or effectively unlimited calls. This is common: free tiers tighten over time, so always re-check a provider's current docs rather than trusting an old tutorial (including older versions of this article).

Do free currency APIs support cryptocurrency rates?

ECB-based sources such as Frankfurter and the ECB feed cover fiat currencies only. Some aggregators like exchangerate.host and CurrencyAPI include crypto on their free tiers, but for serious crypto data a dedicated crypto market-data provider is more reliable. Treat fiat and crypto as separate data sources.

How often should I call a currency exchange rate API?

For ECB-based daily rates, once per day after the roughly 16:00 CET publish is enough — cache the result and serve it to every user. Calling the endpoint on each pageview wastes quota and returns identical numbers. Cache in Redis or your database with a 12-24 hour TTL and keep the last good response as a fallback.

Share this article