8 min read

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.

Try the API →


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 →

C
Written by
Currency Converter Team
Financial Technology Experts

We are a team of financial technology developers dedicated to providing accurate, real-time currency conversion tools. Our mission is to make financial data accessible to everyone.