Banks rarely call it a fee, but the gap between the price they buy currency at and the price they sell it at is exactly that. It is called the spread, and it varies a lot between banks, between currencies and between transfers and cash.
This post measures spreads across every covered bank in a country using AllRatesToday's commercial bank data, which covers 1,219 banks in 124 countries.
Two numbers per row
Each currency row from /api/v1/bank-rates/{country} includes two numbers worth tracking:
-
spread_pct: (sell − buy) ÷ mid × 100. This is the bank's margin on a round trip. -
vs_official_pct: how far the bank's mid sits from the central bank's official rate for the same currency.
Ranking banks by spread
import requests, statistics KEY = "your_api_key" H = {"Authorization": f"Bearer {KEY}"} def spreads(country, ccy): d = requests.get(f"https://allratestoday.com/api/v1/bank-rates/{country}", headers=H).json() out = [] for b in d["banks"]: r = next((x for x in b["rates"] if x["ccy"] == ccy), None) if r: out.append((b["name"], r["spread_pct"], r["vs_official_pct"])) return sorted(out, key=lambda t: t[1]) rows = spreads("in", "USD") for name, spread, vs in rows: print(f"{name:40} spread {spread:5.2f}% vs official {vs:+.2f}%") print("median spread:", statistics.median(s for _, s, _ in rows)) Run the same thing for GBP, AED or JPY and you will usually see spreads widen on less traded currencies.
Going back in time
Every bank board is stored daily. Add ?date=YYYY-MM-DD to a single-bank request to see the board a bank showed on a past day. Daily history as JSON, CSV or XLSX is available on paid plans:
curl -H "Authorization: Bearer $KEY" \ "https://allratestoday.com/api/v1/bank-rates/in/sbi/history?ccy=USD&from=2026-10-01&format=csv" Why the official rate is in every row
A spread on its own tells you how wide a bank's prices are, not where they sit. Pairing each board with the central bank's official rate gives a fixed reference point, and the live mid-market rate from /api/v1/rates gives a second one. All three layers come from one API key, which is what makes this kind of analysis a few lines of code instead of a scraping project.
Bank and country codes: bank codes list · Free key: allratestoday.com/register


