LearnTutorial

How to get exchange rates in Python

There is no single best Python currency converter library: use urllib and a JSON rates API for a small converter, or requests if you already use it. The first exchangerate.dev call can be anonymous; forex-python is another option if you already use it, but installing it does not connect it to this API.

1 Oct 2026 · 5 min read

The shortest path to exchange rates in Python is a GET request to /v1/latest/USD. You get back JSON with per-currency sources and effective_at metadata, so the code can retain the context around each value. Your first call can be anonymous.

  • One GET to /v1/latest/{base} returns rates for 144 currencies as JSON.
  • Use sources[currency] and effective_at[currency] to describe the displayed currency; market_session describes the trading session.
  • The free tier is 5,000 calls a month at 10 requests a minute, no card required.
  • Rates are indicative, published for reference and analytics, not as a dealing quote.

Your first call, no dependencies

You need nothing beyond the standard library. urllib.request handles a single call. To build a complete threshold monitor with saved state, see exchange-rate alerts in Python. Start with this request:

python · standard library
import jsonfrom decimal import Decimalimport urllib.requesturl = "https://api.exchangerate.dev/v1/latest/USD?symbols=EUR,GBP"with urllib.request.urlopen(url, timeout=10) as response:    data = json.load(response, parse_float=Decimal)print(data["rates"]["EUR"])print(data["sources"]["EUR"], data["effective_at"]["EUR"])print(data["market_session"])

That runs without a key. Anonymous calls are capped per IP address, so for anything past a quick test you want a free key.

With requests and an API key

Most projects already use requests. Install requests, set EXCHANGERATE_API_KEY in your server environment, and pass the key as a bearer token:

python · requests
# Install first: python -m pip install requestsimport osfrom decimal import Decimalimport requestsresponse = requests.get(    "https://api.exchangerate.dev/v1/latest/USD",    headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"},    timeout=10,    allow_redirects=False,)if response.status_code == 429:    raise RuntimeError(f"Rate limited; Retry-After: {response.headers.get('Retry-After')}")if 300 <= response.status_code < 400:    raise RuntimeError("Unexpected redirect")response.raise_for_status()data = response.json(parse_float=Decimal)print(data["rates"]["GBP"], data["effective_at"]["GBP"])

A free key gives your application an account-level quota. Send it as a bearer token; the X-API-Key header is also accepted by platforms that cannot set Authorization.

Convert an amount

To convert rather than list rates, call /v1/convert. It returns the rate and the converted amount together:

python · convert 100 USD to EUR
import osfrom decimal import Decimalimport requestsresponse = requests.get(    "https://api.exchangerate.dev/v1/convert/USD/EUR/100",    headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"},    timeout=10,    allow_redirects=False,)if not 200 <= response.status_code < 300:    raise RuntimeError(f"HTTP {response.status_code}; Retry-After: {response.headers.get('Retry-After')}")data = response.json(parse_float=Decimal)print(data["rate"], data["converted"])

If you already use forex-python

The forex-python documentation shows CurrencyRates().get_rate("USD", "EUR") and convert() for its configured source. Installing that package does not connect it to exchangerate.dev. To use this API, call its HTTP endpoint directly with requests or the standard library, as above.

For money calculations, parse decimal values with Decimal and preserve the original rate. Choose rounding rules for the target currency at the final amount boundary. A Python library’s presence does not tell you how recent its upstream observation is; check the response’s source and observation fields.

Know how fresh a rate is

For a displayed currency, read sources[currency] and effective_at[currency]. The aggregate source field describes the least-fresh contributing data. source: live (spot), ecb_daily (ECB fix), market_daily (stored daily market-feed close for currencies without an ECB fix). market_session is open, weekend, or interbank_closed for a known non-weekend closure.

Field valueWhat it meansWhen it moves
source: liveIndicative spot rateIntraday (~60s), trading week
source: ecb_dailyOfficial reference fixOnce per business day
source: market_dailyStored close, no ECB fixDaily market feed
market_session: weekendSaturday or Sunday (interbank closed)Reference fix carries Friday's value; live feed not updating
Indicative, not for settlement

Rates are published for reference, analytics, and display. They are not a dealing quote and should not be used to settle a trade. Every response carries this in its notice field.

Read a single past day

Put a past date in the path. This example needs Basic or above. Plus and Pro reach 1999 where the pair has data. The response adds is_forward_filled when it uses an earlier observation for the requested day, such as a weekend or holiday:

python · rate on a past date
import osfrom decimal import Decimalimport requestsresponse = requests.get(    "https://api.exchangerate.dev/v1/2026-03-14/USD?symbols=JPY",    headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"},    timeout=10,    allow_redirects=False,)if not 200 <= response.status_code < 300:    raise RuntimeError(f"HTTP {response.status_code}; Retry-After: {response.headers.get('Retry-After')}")data = response.json(parse_float=Decimal)print(data["rates"]["JPY"], data["is_forward_filled"])

For a full series across a date range, use /v1/range with start_date and end_date. The historical time-series guide shows pagination and the 366-row JSON page limit.

Questions

Do I need an API key to get exchange rates in Python?

No. You can call /v1/latest/{base} anonymously to start. Anonymous calls are rate-limited per IP address, so for production use you should sign up for a free key, which raises the limit to 5,000 calls a month at 10 requests a minute.

How do I pass my API key in a Python request?

Send it as a bearer token in the Authorization header, for example {"Authorization": "Bearer exr_live_..."}. An X-API-Key header is also accepted for platforms that cannot set Authorization.

Which currencies does the API return?

A single /v1/latest call returns rates for 144 currencies against your chosen base. You can also convert between any supported pair with /v1/convert, including triangulated crosses.

Are the rates suitable for executing trades?

No. The rates are indicative, published for reference, analytics, and display. They are not a dealing quote. Every response says so in its notice field.

Keep reading

Start with a free API key

5,000 requests a month on the free plan. No credit card.

Get a free key