GuidesCURRENCY APIS

Free currency APIs tested from the browser

We tested 12 free currency exchange rate APIs from a browser on 26 October 2026: 11 send CORS headers and work with fetch() directly, only the ECB XML feed needs a proxy, and 8 need no API key. Frankfurter is the default: no key, 228 currencies, one request per conversion. Keyed free tiers give just 100 to 1,000 requests a month.

Every rates API we tested

We ran each endpoint on 26 October 2026 with curl and an Origin: https://example.com header from a German datacenter IP and a US residential IP, then in Chromium 147.0.7727.15 from http://127.0.0.1:8001. More browser-ready endpoints are in our list of 70 free public APIs.

Free exchange rate APIs tested from a browser with fetch(), checked October 2026
APIKeyCORSFree limitUpdatesNotes
FrankfurterNoneYes, ACAO *No quota publishedAs each source publishes228 currencies from 106 sources in v2; open source
fawazahmed0 currency-apiNoneYes, ACAO *No rate limitsDaily340 currencies incl. crypto; use the pages.dev fallback
ExchangeRate-API open accessNoneYes, ACAO *Hourly requests never limited; a 429 lasts 20 minutesEvery 24 hours166 currencies; attribution link required
ECB reference rates (XML)NoneNo, use a proxyOne small XML fileAbout 16:00 CET on working days29 currencies against the euro
exchangerate.funNoneYes, ACAO *No rate limitsHourly172 currencies; behind a Cloudflare bot check
ohmyfinNoneYes, ACAO *No quota publishedECB working daysECB rates with convert and history endpoints
FloatRatesNoneYes, ACAO *No quota publishedEvery 12 hours146 currencies, one JSON file per base
Coinbase exchange-ratesNoneYes, ACAO *Public, no authLive659 rates incl. crypto
exchangerate.hostAccess key, now requiredYes, ACAO *100 requests/monthDailyNon-commercial on the free plan
FixerAccess keyYes, ACAO *100 requests/monthHourlyKey goes in access_key=
Open Exchange RatesApp IDYes, ACAO *1,000 requests/monthHourlyUSD base only on the free plan
CurrencyAPI.comAPI keyYes, echoes Origin300 requests/monthNot stated for freeIncludes historical rates; non-commercial

Frankfurter: convert without a key

Frankfurter (https://api.frankfurter.dev) needs no key, sends Access-Control-Allow-Origin: *, publishes no quota, and is open source if you want to self-host it. The old host api.frankfurter.app now answers with a 301 redirect, so update old code.

Version 2 covers 228 currencies from 106 central banks and official sources, with data back to 1948. GET /v2/rate/usd/eur returned {"date":"2026-10-26","base":"USD","quote":"EUR","rate":0.89153}. By default v2 blends its sources; add ?providers=ecb for European Central Bank rates only, which on Monday 26 October returned Friday's rate, 0.89342 for 2026-10-23. The older v1 still answers and lists 30 currencies.

Convert an amount with Frankfurter, no key
const amount = 250;
const from = 'usd';
const to = 'eur';

const response = await fetch(`https://api.frankfurter.dev/v2/rate/${from}/${to}`);
const body = await response.json().catch(() => null);

if (!response.ok) {
  throw new Error(`Frankfurter failed: ${body?.message ?? response.status}`);
}

console.log(`${amount} ${body.base} = ${(amount * body.rate).toFixed(2)} ${body.quote}`);
console.log(`rate ${body.rate} for ${body.date}`);

fawazahmed0 currency-api with a fallback

The fawazahmed0 currency-api is a set of static JSON files on jsDelivr, such as https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json. No key, no rate limits, updated daily, Access-Control-Allow-Origin: *, and 340 currencies including crypto. For a past date, replace @latest with a date like @2024-03-06.

Its README asks you to fall back to https://latest.currency-api.pages.dev/v1/currencies/usd.json when jsDelivr fails. Direct calls worked every time in our test; through the cors.dev proxy, jsDelivr failed once with 502 upstream_unreachable and then worked, and since the files send CORS headers, a proxy adds nothing here.

fawazahmed0 currency-api with its documented fallback mirror
const base = 'usd';
const mirrors = [
  `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/${base}.min.json`,
  `https://latest.currency-api.pages.dev/v1/currencies/${base}.min.json`,
];

let rates = null;

for (const url of mirrors) {
  try {
    const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
    if (response.ok) {
      rates = await response.json();
      break;
    }
    console.warn(`${url} answered ${response.status}`);
  } catch (error) {
    console.warn(`${url} failed: ${error.message}`);
  }
}

if (!rates) {
  throw new Error('Both currency-api mirrors failed');
}

console.log(rates.date, { eur: rates[base].eur, gbp: rates[base].gbp, btc: rates[base].btc });

ExchangeRate-API open access rules

https://open.er-api.com/v6/latest/USD needs no key, sends Access-Control-Allow-Origin: * and covers 166 currencies. Its terms:

  • Data refreshes once every 24 hours, and each response includes time_next_update_utc.
  • Request once a day, or at most once an hour, and you never get rate limited.
  • A rate-limited IP gets HTTP 429, lifted after 20 minutes.
  • Show the attribution link Rates By Exchange Rate API.
  • Caching is allowed, and so are personal and commercial conversions; redistributing the data is not.

ECB reference rates through a proxy

The European Central Bank publishes https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml with no key but no CORS headers, so Chromium 147 blocked it: No 'Access-Control-Allow-Origin' header is present on the requested resource. Prefix the URL with https://proxy.cors.dev/ and it returns 200, text/xml, 1,547 bytes; the 90-day history file eurofxref-hist-90d.xml (70,590 bytes) works too. The free CORS proxy needs no key or signup and handles GET and HEAD for text responses up to 1 MiB, with a 10-second deadline and 600 requests per minute per client IP.

The ECB usually updates the file around 16:00 CET every working day, except TARGET closing days, after a concertation between central banks around 14:10 CET. On Monday 26 October before 16:00 CET, the file was still dated 23 October and quoted 29 currencies against the euro. The ECB publishes these rates for information purposes only and strongly discourages using them for transactions.

ECB reference rates (XML) through the proxy, parsed with DOMParser
const proxy = 'https://proxy.cors.dev/';
const url = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';

const response = await fetch(proxy + url, { credentials: 'omit' });

if (!response.ok) {
  throw new Error(`ECB via proxy failed: ${response.headers.get('X-Cors-Error') ?? response.status}`);
}

const xml = new DOMParser().parseFromString(await response.text(), 'application/xml');
const date = xml.querySelector('Cube[time]').getAttribute('time');
const rates = [...xml.querySelectorAll('Cube[currency]')].map((cube) => ({
  currency: cube.getAttribute('currency'),
  perEuro: Number(cube.getAttribute('rate')),
}));

console.log('ECB reference rates for', date);
console.table(rates);

Keyed free tiers run out fast

All four keyed APIs answered a fake key with 401 and CORS headers, so they work from the browser once you have a key:

  • exchangerate.host now needs an access key: 100 requests per month, daily updates, non-commercial use.
  • Fixer: access key, 100 calls per month, hourly updates.
  • Open Exchange Rates: app_id, 1,000 requests per month, hourly updates, USD base only.
  • CurrencyAPI.com: apikey, 300 requests per month including historical rates; commercial use needs an upgrade.

Polling once an hour costs about 720 requests a month (24 x 30), so a 100 to 300 request quota only works with a shared cache on your backend. A key in frontend code is visible to every visitor, who can copy it and spend your quota; see when a frontend needs an API proxy. These APIs already send CORS headers, and the cors.dev free tier refuses URLs with key=, apikey=, access_key= and similar parameters (HTTP 400, X-Cors-Error: credential_in_url).

Crypto and other no-key sources

Coinbase (https://api.coinbase.com/v2/exchange-rates?currency=USD) needs no auth and returned 659 rates, fiat and crypto, in 17,370 bytes. For crypto prices, see crypto price APIs from the browser. FloatRates (https://www.floatrates.com/daily/usd.json) returned 146 currencies and updates every 12 hours.

ohmyfin (https://ohmyfin.org/api/exchange/latest?base=USD&symbols=EUR) re-serves ECB rates with an attribution field and has convert, pair and historical endpoints; convert?from=USD&to=EUR&amount=100 returned 89.341553 for 2026-10-23. exchangerate.fun (https://api.exchangerate.fun/latest?base=USD) says it updates hourly with no rate limits, for personal and commercial use, and returned 172 currencies. It sits behind a Cloudflare bot check: headless Chromium on our datacenter IP got a 403 challenge page, which the browser reports as a CORS error, while a residential IP got 200.

Good questions.

Which currency API is free and needs no API key?

Eight of the 12 we tested: Frankfurter, the fawazahmed0 currency-api, ExchangeRate-API open access, the ECB XML feed, exchangerate.fun, ohmyfin, FloatRates and Coinbase. Frankfurter is the easiest default with 228 currencies and Access-Control-Allow-Origin: *.

When do ECB exchange rates update?

Usually around 16:00 CET on every working day, except TARGET closing days. Before 16:00 CET on a Monday, you still get Friday's rates.

Can I use free exchange rates for payments or invoices?

The ECB says its reference rates are for information only and strongly discourages using them for transactions. ExchangeRate-API open access and exchangerate.fun allow commercial use; the free plans of exchangerate.host and CurrencyAPI.com are non-commercial.

Do I have to credit the rate provider?

Check each provider's terms. ExchangeRate-API open access requires a visible Rates By Exchange Rate API link, and ohmyfin returns an attribution line for the ECB data with every response.

Why does the ECB XML file fail with a CORS error?

www.ecb.europa.eu sends no Access-Control-Allow-Origin header, so the browser won't let fetch() read the file. Fetch it through https://proxy.cors.dev/, or use Frankfurter with ?providers=ecb or ohmyfin, which serve ECB rates with CORS headers.

How often should my app fetch exchange rates?

ECB-based sources and ExchangeRate-API change once a day and FloatRates every 12 hours, so polling more than once an hour wastes requests. ExchangeRate-API open access never rate limits hourly or daily polling.