Currency Converter API for Python — Free Exchange Rates, No API Key
Use the free Currency Exchange Tool API in Python with requests. No API key, no signup. Live rates for 100+ currencies. pandas DataFrame integration and error handling included.
Currency Converter API for Python — Free, No Key, No Signup
Last updated: June 28, 2026 — Read time: 8 min
Python is the most popular language for data analysis and automation. If you need live exchange rates in your Python scripts, Jupyter notebooks, or data pipelines, the Currency Exchange Tool API gives you exactly that — no API key, no signup, no credit card.
This guide covers requests-based conversion, pandas DataFrame integration, caching strategies, and error handling.
Quick Start — 4 Lines
import requests
res = requests.get(
'https://www.currencyexchangetool.com/api/v1/convert',
params={'amount': 100, 'from': 'USD', 'to': 'EUR'}
)
data = res.json()
print(f"100 USD = {data['result']} EUR at rate {data['rate']}")
No API key, no headers, no registration. Just a GET request with query parameters.
Complete Python Client
import requests
from typing import Optional, Dict, Any, List
from datetime import datetime
class CurrencyAPI:
"""Free currency exchange API client — no API key required."""
BASE_URL = 'https://www.currencyexchangetool.com/api/v1'
def __init__(self):
self.session = requests.Session()
def convert(self, amount: float, from_currency: str, to_currency: str) -> Dict[str, Any]:
"""Convert an amount between two currencies at the live rate."""
if amount <= 0:
raise ValueError('Amount must be positive')
res = self.session.get(
f'{self.BASE_URL}/convert',
params={'amount': amount, 'from': from_currency, 'to': to_currency},
)
# Check rate limits
remaining = res.headers.get('X-RateLimit-Remaining')
if remaining == '0':
retry_after = int(res.headers.get('Retry-After', 60))
raise RuntimeError(f'Rate limited. Wait {retry_after}s.')
res.raise_for_status()
data = res.json()
if not data.get('success'):
raise ValueError(f"API error [{data.get('code')}]: {data.get('error')}")
return {
'from': data['from'],
'to': data['to'],
'amount': data['amount'],
'rate': data['rate'],
'result': data['result'],
'change_24h_pct': data.get('changePct24h'),
'updated_at': datetime.fromisoformat(
data['updatedAt'].replace('Z', '+00:00')
),
}
def get_currencies(self) -> List[Dict[str, str]]:
"""Return all supported currencies (code + name)."""
res = self.session.get(f'{self.BASE_URL}/currencies')
res.raise_for_status()
data = res.json()
if not data.get('success'):
raise RuntimeError('Failed to fetch currencies')
return data['currencies']
def get_history(
self,
from_currency: str,
to_currency: str,
days: Optional[int] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
) -> List[Dict[str, Any]]:
"""Get historical daily rates. Use days OR (start_date + end_date)."""
params = {'from': from_currency, 'to': to_currency}
if days:
params['days'] = days
elif start_date and end_date:
params['start_date'] = start_date
params['end_date'] = end_date
else:
params['days'] = 30 # default
res = self.session.get(f'{self.BASE_URL}/history', params=params)
res.raise_for_status()
data = res.json()
if not data.get('success'):
raise RuntimeError('Failed to fetch history')
return data['data']
Usage
api = CurrencyAPI()
# Convert 1000 USD to UAH
result = api.convert(1000, 'USD', 'UAH')
print(f"1000 USD = {result['result']} UAH (rate: {result['rate']})")
# List currencies
currencies = api.get_currencies()
print(f"{len(currencies)} currencies: {[c['code'] for c in currencies[:5]]}...")
# Get 90-day history for EUR/USD
history = api.get_history('EUR', 'USD', days=90)
print(f"Got {len(history)} days of EUR/USD data")
pandas DataFrame Integration
The history endpoint is designed for analysis. Here is how to load it into pandas:
import pandas as pd
from datetime import datetime
api = CurrencyAPI()
# Fetch 1 year of USD/UAH history
raw = api.get_history('USD', 'UAH', days=365)
# Convert to DataFrame
df = pd.DataFrame(raw)
df['date'] = pd.to_datetime(df['date'])
df = df.set_index('date').sort_index()
# Basic analysis
print(f"Period: {df.index[0].date()} → {df.index[-1].date()}")
print(f"High: {df['rate'].max():.4f}")
print(f"Low: {df['rate'].min():.4f}")
print(f"Mean: {df['rate'].mean():.4f}")
print(f"Std: {df['rate'].std():.4f}")
print(f"Volatility: {df['rate'].std() / df['rate'].mean() * 100:.2f}%")
# Latest rate
print(f"\nLatest rate ({df.index[-1].date()}): {df['rate'].iloc[-1]:.4f}")
Visualize with Matplotlib
import matplotlib.pyplot as plt
df['rate'].plot(figsize=(12, 5), title='USD/UAH Exchange Rate — Last 365 Days')
plt.ylabel('UAH per 1 USD')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Multi-Currency Portfolio Tracker
Fetch rates for multiple pairs and build a portfolio snapshot:
api = CurrencyAPI()
portfolio = {
'USD': 1000, # 1000 USD base
'EUR': 500, # 500 EUR
'GBP': 300, # 300 GBP
'UAH': 50000, # 50,000 UAH
}
# Convert everything to USD
total_usd = portfolio['USD'] # start with USD
for currency, amount in portfolio.items():
if currency == 'USD':
continue
result = api.convert(amount, currency, 'USD')
usd_value = result['result']
print(f"{amount:>10,.2f} {currency} = {usd_value:>10,.2f} USD (rate: {result['rate']})")
total_usd += usd_value
print(f"\n{'Total portfolio':>20}: {total_usd:>10,.2f} USD")
Caching to Avoid Rate Limits
The API has a 100 req/min limit. For scripts that re-run frequently, cache results:
import json
from pathlib import Path
from datetime import datetime, timedelta
class CachedCurrencyAPI(CurrencyAPI):
"""CurrencyAPI with a simple file-based cache (TTL: 60 seconds)."""
def __init__(self, cache_dir: str = '.cache'):
super().__init__()
self.cache_dir = Path(cache_dir)
self.cache_dir.mkdir(exist_ok=True)
def _cache_path(self, from_c: str, to_c: str) -> Path:
return self.cache_dir / f'{from_c}_{to_c}.json'
def convert(self, amount: float, from_currency: str, to_currency: str) -> dict:
cache_path = self._cache_path(from_currency, to_currency)
# Read cache if fresh
if cache_path.exists():
cached = json.loads(cache_path.read_text())
age = datetime.now() - datetime.fromisoformat(cached['cached_at'])
if age < timedelta(seconds=60):
# Recalculate result with cached rate
cached['amount'] = amount
cached['result'] = amount * cached['rate']
return cached
# Fetch fresh rate
result = super().convert(amount, from_currency, to_currency)
result['cached_at'] = datetime.now().isoformat()
# Write cache
cache_path.write_text(json.dumps(result, default=str))
return result
Error Handling Cheat Sheet
import requests
def safe_convert(amount, from_c, to_c):
try:
res = requests.get(
'https://www.currencyexchangetool.com/api/v1/convert',
params={'amount': amount, 'from': from_c, 'to': to_c},
timeout=10,
)
data = res.json()
if res.status_code == 429:
wait = res.headers.get('Retry-After', 60)
return {'error': f'Rate limited. Wait {wait}s.'}
if not data.get('success'):
return {'error': f"{data.get('code')}: {data.get('error')}"}
return {'rate': data['rate'], 'result': data['result']}
except requests.Timeout:
return {'error': 'Request timed out'}
except requests.ConnectionError:
return {'error': 'Network error — check your connection'}
except Exception as e:
return {'error': str(e)}
Use Cases for Python Developers
- Jupyter notebooks — pull live rates for financial analysis
- Airflow / Prefect pipelines — no API key means one less secret to manage
- Django / Flask apps — add currency conversion without paid API tiers
- Trading bots — use the history endpoint for backtesting
- Portfolio trackers — convert multi-currency holdings to a base currency
📖 Full API documentation → 💱 Try the live converter → 🔓 Why this API is free →