If you're building a Python application that deals with money across borders — whether it's an e-commerce platform, a fintech dashboard, or a data pipeline — you need reliable, real-time exchange rates. This guide walks you through two practical approaches to fetching live currency rates in Python, plus production-ready patterns for caching, error handling, and framework integration.
Why You Need Live Exchange Rates in Python
Hardcoding exchange rates is a non-starter. Rates fluctuate constantly, and even a small drift can compound into significant errors at scale. Common scenarios where you need live rates include:
- E-commerce checkout: Converting product prices for international customers.
- Financial reporting: Generating accurate multi-currency balance sheets.
- Data analysis: Normalizing revenue data across regions for dashboards.
- Payment processing: Calculating settlement amounts between currencies.
- Travel apps: Showing users real-time conversion estimates.
The right approach is to pull rates from a reliable API, cache them sensibly, and handle failures gracefully. Let's look at how.
Option 1: Using the Requests Library (Raw HTTP)
The most straightforward way is to call an exchange rate REST API directly with the requests library. AllRatesToday provides a clean JSON API that supports 160+ currencies with rates updated every 60 seconds.
Installation
pip install requests
Fetching the Latest Rates
import requests
API_KEY = "your_api_key_here"
BASE_URL = "https://api.allratestoday.com/v1"
def get_latest_rates(base_currency="USD"):
"""Fetch the latest exchange rates for a given base currency."""
response = requests.get(
f"{BASE_URL}/latest",
params={"base": base_currency},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
response.raise_for_status()
data = response.json()
return data["rates"]
rates = get_latest_rates("USD")
print(f"USD to EUR: {rates['EUR']}")
print(f"USD to GBP: {rates['GBP']}")
print(f"USD to JPY: {rates['JPY']}")
Converting Between Currencies
def convert_currency(amount, from_currency, to_currency):
"""Convert an amount from one currency to another."""
rates = get_latest_rates(from_currency)
if to_currency not in rates:
raise ValueError(f"Unsupported currency: {to_currency}")
return round(amount * rates[to_currency], 2)
# Convert 100 USD to EUR
result = convert_currency(100, "USD", "EUR")
print(f"100 USD = {result} EUR")
This approach gives you full control over the HTTP request, headers, and response parsing. It works well when you want to keep dependencies minimal or need to customize the request behavior.
Option 2: Using the AllRatesToday Python SDK
For a cleaner developer experience, the AllRatesToday Python SDK wraps the API with a Pythonic interface, built-in error handling, and automatic retries.
Installation
pip install allratestoday
Basic Usage
from allratestoday import AllRatesToday
client = AllRatesToday("your_api_key_here")
# Get latest rates
rates = client.get_rates(base="USD")
print(f"USD to EUR: {rates['EUR']}")
# Convert directly
result = client.convert(amount=250, from_currency="GBP", to_currency="JPY")
print(f"250 GBP = {result} JPY")
Fetching Historical Rates
# Get rates for a specific date
historical = client.get_rates(base="EUR", date="2026-01-15")
print(f"EUR to USD on Jan 15: {historical['USD']}")
The SDK handles authentication, request formatting, and response parsing for you. It's the recommended approach for most projects.
Caching Rates to Minimize API Calls
Fetching rates on every request is wasteful and will burn through your API quota quickly. Exchange rates don't change every millisecond — a smart cache with a short TTL is the right pattern.
Simple In-Memory Cache
import time
from allratestoday import AllRatesToday
client = AllRatesToday("your_api_key_here")
_cache = {}
CACHE_TTL = 300 # 5 minutes
def get_cached_rates(base="USD"):
"""Return cached rates if fresh, otherwise fetch new ones."""
now = time.time()
cache_key = f"rates_{base}"
if cache_key in _cache:
cached_data, timestamp = _cache[cache_key]
if now - timestamp < CACHE_TTL:
return cached_data
rates = client.get_rates(base=base)
_cache[cache_key] = (rates, now)
return rates
Redis Cache for Multi-Instance Deployments
If you're running multiple server instances (e.g., behind a load balancer), an in-memory cache won't be shared between them. Use Redis instead:
import json
import redis
from allratestoday import AllRatesToday
r = redis.Redis(host="localhost", port=6379, db=0)
client = AllRatesToday("your_api_key_here")
def get_rates_with_redis(base="USD", ttl=300):
"""Fetch rates with Redis caching."""
cache_key = f"exchange_rates:{base}"
cached = r.get(cache_key)
if cached:
return json.loads(cached)
rates = client.get_rates(base=base)
r.setex(cache_key, ttl, json.dumps(rates))
return rates
Choose your cache TTL based on your use case: 60 seconds for near-real-time applications, 5 minutes for e-commerce, or up to 24 hours for accounting and reporting.
Handling Errors and Edge Cases
Production code needs to handle network timeouts, invalid API keys, rate limits, and unexpected responses. Here's a robust pattern:
import requests
from requests.exceptions import HTTPError, Timeout, ConnectionError
API_KEY = "your_api_key_here"
BASE_URL = "https://api.allratestoday.com/v1"
def get_rates_safe(base="USD", retries=3):
"""Fetch rates with retry logic and proper error handling."""
for attempt in range(retries):
try:
response = requests.get(
f"{BASE_URL}/latest",
params={"base": base},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
if response.status_code == 429:
wait = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Retrying in {wait}s...")
time.sleep(wait)
continue
response.raise_for_status()
return response.json()["rates"]
except Timeout:
print(f"Timeout on attempt {attempt + 1}/{retries}")
except ConnectionError:
print(f"Connection error on attempt {attempt + 1}/{retries}")
except HTTPError as e:
if e.response.status_code == 401:
raise ValueError("Invalid API key") from e
raise
raise RuntimeError("Failed to fetch rates after all retries")
Key things to handle:
-
HTTP 429 (Too Many Requests): Respect the
Retry-Afterheader and back off. -
Timeouts: Always set a
timeoutparameter. Never let requests hang indefinitely. - Invalid API keys (401): Fail fast and surface a clear error message.
- Network failures: Retry with backoff, then fall back to cached data if available.
Real-World Use Cases
Flask Integration
from flask import Flask, jsonify, request
from allratestoday import AllRatesToday
app = Flask(__name__)
client = AllRatesToday("your_api_key_here")
@app.route("/api/convert")
def convert():
amount = float(request.args.get("amount", 1))
from_curr = request.args.get("from", "USD")
to_curr = request.args.get("to", "EUR")
result = client.convert(
amount=amount,
from_currency=from_curr,
to_currency=to_curr,
)
return jsonify({"result": result, "from": from_curr, "to": to_curr})
Django View
from django.http import JsonResponse
from allratestoday import AllRatesToday
client = AllRatesToday("your_api_key_here")
def convert_view(request):
amount = float(request.GET.get("amount", 1))
from_curr = request.GET.get("from", "USD")
to_curr = request.GET.get("to", "EUR")
result = client.convert(
amount=amount,
from_currency=from_curr,
to_currency=to_curr,
)
return JsonResponse({"result": result, "from": from_curr, "to": to_curr})
Data Analysis with Pandas
import pandas as pd
from allratestoday import AllRatesToday
client = AllRatesToday("your_api_key_here")
rates = client.get_rates(base="USD")
# Convert a DataFrame column from local currencies to USD
df = pd.DataFrame({
"revenue": [1000, 2500, 800, 3200],
"currency": ["EUR", "GBP", "JPY", "CAD"],
})
df["usd_amount"] = df.apply(
lambda row: row["revenue"] / rates[row["currency"]], axis=1
)
print(df)
Performance Tips
Cache aggressively. A 5-minute cache eliminates 99% of redundant API calls without meaningful data staleness for most applications.
Batch your requests. Fetch all rates for a base currency in one call rather than making separate calls for each currency pair.
-
Use connection pooling. If you're using
requestsdirectly, use aSessionobject to reuse TCP connections:
session = requests.Session() session.headers.update({"Authorization": f"Bearer {API_KEY}"}) # Reuse `session` for all API calls -
Store your API key securely. Use environment variables or a secrets manager — never hardcode keys in source files.
import os API_KEY = os.environ["ALLRATESTODAY_API_KEY"] Set timeouts on every request. A missing timeout can hang your entire application if the API is slow to respond.
Log API response times. Monitor latency in production so you can spot degradation early.
Wrapping Up
Getting live exchange rates into your Python application takes just a few lines of code. Whether you use the requests library for maximum control or the AllRatesToday Python SDK for convenience, the fundamentals are the same: fetch from a reliable source, cache smartly, handle errors, and keep your API key secure.
AllRatesToday offers a free tier with no credit card required, covering 160+ currencies with updates every 60 seconds. Sign up at allratestoday.com and start building.
Top comments (0)