CurrencyExchange RatesJavaScriptPythonTutorial

Currency Conversion API: JavaScript and Python Examples with Live Rates

ApogeoAPI6 min read

Getting exchange rates from the API

ApogeoAPI's exchange rates endpoint returns current rates for all supported currencies relative to USD:

GET https://api.apogeoapi.com/v1/rates/USD?apikey=YOUR_KEY

# Response
{
  "base": "USD",
  "date": "2026-05-14",
  "rates": {
    "EUR": 0.923,
    "GBP": 0.789,
    "JPY": 154.2,
    "BRL": 4.95,
    "ARS": 892.5,
    "AUD": 1.54,
    ...160 more currencies...
  }
}

Free tier: 1,000 requests/month. Rates refresh every 4 hours.

JavaScript — simple converter

const APOGEO_KEY = process.env.APOGEO_KEY;
let ratesCache = null;
let ratesCachedAt = 0;

async function getRates(base = 'USD') {
  // Cache rates for 4 hours
  if (ratesCache && Date.now() - ratesCachedAt < 4 * 60 * 60 * 1000) {
    return ratesCache;
  }

  const res = await fetch(
    `https://api.apogeoapi.com/v1/rates/${base}?apikey=${APOGEO_KEY}`
  );
  const data = await res.json();
  ratesCache = data.rates;
  ratesCachedAt = Date.now();
  return ratesCache;
}

async function convert(amount, from, to) {
  const rates = await getRates('USD');

  // Convert to USD first, then to target
  const inUSD = from === 'USD' ? amount : amount / rates[from];
  const result = to === 'USD' ? inUSD : inUSD * rates[to];

  return Math.round(result * 100) / 100; // round to 2 decimal places
}

// Usage
const inEUR = await convert(29.99, 'USD', 'EUR'); // → 27.67
const inJPY = await convert(29.99, 'USD', 'JPY'); // → 4623
console.log(`${inEUR} EUR`);
console.log(`¥${inJPY}`);

JavaScript — format currency amounts

Use the browser's Intl.NumberFormat to format amounts with the correct symbol, thousands separator, and decimal places for any locale:

function formatCurrency(amount, currencyCode, locale = 'en-US') {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currencyCode,
    maximumFractionDigits: ['JPY', 'KRW', 'IDR', 'CLP'].includes(currencyCode) ? 0 : 2,
  }).format(amount);
}

// Examples
formatCurrency(29.99, 'USD');   // "$29.99"
formatCurrency(27.67, 'EUR');   // "€27.67"
formatCurrency(4623, 'JPY');    // "¥4,623"
formatCurrency(149.50, 'BRL', 'pt-BR'); // "R$ 149,50"

JavaScript — React hook for currency conversion

// hooks/useExchangeRate.ts
import { useState, useEffect } from 'react';

const cache = new Map<string, { rates: Record<string, number>; expires: number }>();

export function useExchangeRate(baseCurrency = 'USD') {
  const [rates, setRates] = useState<Record<string, number> | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const cached = cache.get(baseCurrency);
    if (cached && cached.expires > Date.now()) {
      setRates(cached.rates);
      setLoading(false);
      return;
    }

    fetch(`/api/rates?base=${baseCurrency}`) // proxy through your API
      .then(r => r.json())
      .then(data => {
        cache.set(baseCurrency, { rates: data.rates, expires: Date.now() + 4 * 3600000 });
        setRates(data.rates);
        setLoading(false);
      });
  }, [baseCurrency]);

  const convert = (amount: number, from: string, to: string): number => {
    if (!rates) return amount;
    const inBase = from === baseCurrency ? amount : amount / rates[from];
    return to === baseCurrency ? inBase : inBase * rates[to];
  };

  return { rates, loading, convert };
}

// Component
function PriceDisplay({ usdAmount }: { usdAmount: number }) {
  const { convert, loading } = useExchangeRate('USD');
  const [targetCurrency, setTargetCurrency] = useState('EUR');

  if (loading) return <span>Loading...</span>;

  const converted = convert(usdAmount, 'USD', targetCurrency);

  return (
    <div>
      <span>{formatCurrency(converted, targetCurrency)}</span>
      <select value={targetCurrency} onChange={e => setTargetCurrency(e.target.value)}>
        {['EUR', 'GBP', 'JPY', 'BRL', 'CAD', 'AUD'].map(c => (
          <option key={c} value={c}>{c}</option>
        ))}
      </select>
    </div>
  );
}

Python — synchronous (requests)

import requests
import time
from functools import lru_cache
from typing import Optional

APOGEO_KEY = "your_api_key"
BASE_URL = "https://api.apogeoapi.com/v1"

# Simple TTL cache using a dict
_rates_cache: dict = {}
_rates_cached_at: float = 0

def get_rates(base: str = "USD") -> dict[str, float]:
    global _rates_cache, _rates_cached_at

    if _rates_cache and time.time() - _rates_cached_at < 4 * 3600:
        return _rates_cache

    response = requests.get(
        f"{BASE_URL}/rates/{base}",
        params={"apikey": APOGEO_KEY},
        timeout=3,
    )
    response.raise_for_status()
    data = response.json()
    _rates_cache = data["rates"]
    _rates_cached_at = time.time()
    return _rates_cache


def convert(amount: float, from_currency: str, to_currency: str, base: str = "USD") -> float:
    rates = get_rates(base)

    # Normalize to base currency first
    in_base = amount if from_currency == base else amount / rates[from_currency]
    # Convert to target
    result = in_base if to_currency == base else in_base * rates[to_currency]
    return round(result, 2)


# Usage
print(convert(29.99, "USD", "EUR"))  # 27.67
print(convert(29.99, "USD", "BRL"))  # 148.45
print(convert(100, "EUR", "JPY"))    # 16694

Python — async (httpx)

import asyncio
import httpx
import time

APOGEO_KEY = "your_api_key"
_rates_cache: dict = {}
_rates_cached_at: float = 0


async def get_rates_async(base: str = "USD", client: httpx.AsyncClient = None) -> dict:
    global _rates_cache, _rates_cached_at

    if _rates_cache and time.time() - _rates_cached_at < 4 * 3600:
        return _rates_cache

    should_close = client is None
    if client is None:
        client = httpx.AsyncClient()

    try:
        r = await client.get(
            f"https://api.apogeoapi.com/v1/rates/{base}",
            params={"apikey": APOGEO_KEY},
            timeout=3,
        )
        r.raise_for_status()
        data = r.json()
        _rates_cache = data["rates"]
        _rates_cached_at = time.time()
        return _rates_cache
    finally:
        if should_close:
            await client.aclose()


async def main():
    async with httpx.AsyncClient() as client:
        # Fetch rates and geolocate in parallel
        rates_task = get_rates_async(client=client)
        geo_task = client.get(
            "https://api.apogeoapi.com/v1/geo/8.8.8.8",
            params={"apikey": APOGEO_KEY},
        )

        rates, geo_response = await asyncio.gather(rates_task, geo_task)
        geo = geo_response.json()

        currency = geo["currencyCode"]
        usd_price = 29.99
        rate = rates.get(currency, 1)
        local_price = round(usd_price * rate, 2)

        print(f"Country: {geo['countryCode']}")
        print(f"Currency: {currency}")
        print(f"Price: {local_price} {currency}")


asyncio.run(main())

Combining with IP geolocation

ApogeoAPI returns the visitor's currency code in the IP geolocation response — saving you a separate rates call in many cases:

// One call gets country + currency code
const geoRes = await fetch(
  `https://api.apogeoapi.com/v1/geo/${ip}?apikey=${APOGEO_KEY}`
);
const geo = await geoRes.json();
// geo.currencyCode = "BRL"
// geo.currencyRate = 4.95 (relative to USD — same as the exchange rates endpoint!)

// Convert directly from geo response — no second API call needed
const localPrice = Math.round(29.99 * geo.currencyRate);
console.log(`${geo.currencySymbol}${localPrice} ${geo.currencyCode}`);
// → "R$148 BRL"

API docs: api.apogeoapi.com/api/docs. Free tier: app.apogeoapi.com/register.

Try ApogeoAPI free

1,000 requests/month forever. 14-day full-access trial. No credit card.

Get your free API key