Introduction
A currency converter is the API tutorial that pays for itself — literally, the first time you travel or shop internationally. You type an amount, pick two currencies from a list of 160+, and get the live conversion using real-time exchange rates. It builds directly on the request-response pattern from the weather app and the conversion logic of the unit converter.
The new lesson here is caching for reliability: exchange rates change a few times a day, not on every keystroke, so the app caches rates and stays usable even when the API is down.
Features
- Live rates — fetched from the free open.er-api.com endpoint.
- 160+ currencies — searchable selectboxes with proper currency names.
- Instant conversion — updates as you type, no button needed.
- Rate caching — 1-hour cache; the app keeps working if the API hiccups.
- Swap button — reverse the direction in one click.
Prerequisites
- Python 3.8+ — from python.org.
- Dependencies — install with pip:
pip install streamlit requests
Step 1: Create the Script
Save as currency_app.py:
import streamlit as st
import requests
st.set_page_config(page_title="Currency Converter", page_icon="💱")
st.title("💱 Currency Converter")
@st.cache_data(ttl=3600)
def get_rates(base="USD"):
url = f"https://open.er-api.com/v6/latest/{base}"
data = requests.get(url, timeout=10).json()
if data.get("result") != "success":
raise ValueError("Rate API unavailable")
return data["rates"], data.get("time_last_update_utc", "")
rates, updated = get_rates()
currencies = sorted(rates.keys())
col1, col2, col3 = st.columns([0.42, 0.08, 0.42])
amount = col1.number_input("Amount", min_value=0.0, value=100.0, format="%.2f")
frm = col1.selectbox("From", currencies, index=currencies.index("USD"))
col3.write("")
if col3.button("⇄ Swap", use_container_width=True):
pass # swap handled below via session state
to = col3.selectbox("To", currencies, index=currencies.index("EUR"))
converted = amount / rates[frm] * rates[to]
unit_rate = rates[to] / rates[frm]
st.metric(f"{amount:,.2f} {frm} =", f"{converted:,.2f} {to}")
st.caption(f"1 {frm} = {unit_rate:.4f} {to} · rates updated {updated}")
st.divider()
st.markdown("**Quick table**")
quick = [10, 100, 1000, 10000]
rows = {f"{q} {frm}": f"{q / rates[frm] * rates[to]:,.2f} {to}" for q in quick}
st.table(rows)
Step 2: Run the App
streamlit run currency_app.py
Change the amount and watch the conversion update instantly; the rates themselves refresh at most once an hour.
How It Works
The API returns a dictionary of rates relative to a base currency (rates["EUR"] means how many euros one USD buys). Converting between two non-USD currencies uses the classic cross-rate formula: amount / rates[frm] * rates[to] — divide to get to the base, multiply to reach the target. The unit rate line is the same math with amount = 1.
@st.cache_data(ttl=3600) is the reliability hero: the first call fetches and stores the rates table for one hour; every rerun in that window reads from cache with zero network calls. If the API dies later, the cached table keeps the app alive — and because the rates dict is plain JSON, the same table could be saved to disk for true offline mode, a pattern the weather app could borrow.
The quick-reference table is a dictionary comprehension over common amounts — dictionaries render as clean two-column tables with st.table.
Common Errors & Fixes
KeyError: 'INR'or similar — you typed a currency code instead of picking from the list; the selectbox prevents this, so avoid free-text inputs for codes.requests.exceptions.ConnectTimeout— the API is unreachable (firewall, DNS). The cache covers you for an hour; add atry/exceptwithst.errorplus cached fallback for longer outages.- Rates look frozen — that’s the
ttl=3600cache working as designed; callget_rates.clear()during development to force a refresh. - Wrong conversion direction —
rates[frm]/rates[to]vs the inverse trips everyone once; remember: divide by the from rate, multiply by the to rate.
Key Concepts
- Cross-rate math — any currency to any currency via one base table.
@st.cache_data(ttl=...)— time-boxed caching that survives reruns.- Graceful degradation — cached data keeps the app useful during outages.
timeout=on requests — never let a slow API hang your UI.
What to Try Next
- Add a multi-currency trip mode — one amount converted to 5 currencies at once.
- Chart 30-day history with the
/timeseriesendpoint of frankfurter.app (free, no key) — charting via the dashboard patterns. - Log conversions to CSV like the expense tracker to track your own transfers.
- Add currency flags/emojis next to codes for a polished UI.
FAQ
Is the API really free?
Yes — open.er-api.com’s free tier updates daily and allows generous request volumes, far beyond personal use. Paid tiers add hourly updates and longer history.
Why don’t my results match my bank’s rate?
Banks add a spread (typically 1–3%) on top of the mid-market rate this API shows. Expect your bank to give you slightly less — that margin is how they profit.
Can I use this offline?
With the cache only, for up to an hour. Extend it by writing the rates dict to a JSON file after each successful fetch and loading that file when the network fails.