Exchange Rates
The extension provides table functions to fetch exchange rates from 10 different sources - 7 free central bank APIs and 3 paid commercial APIs.
Quick Start
Section titled “Quick Start”-- Load extensionINSTALL monetary FROM community;LOAD monetary;
-- Fetch ECB rates (free, no API key needed)SELECT currency_from, currency_to, rate, valid_fromFROM ecb_exchange_rates()LIMIT 5;Storing Exchange Rates
Section titled “Storing Exchange Rates”For production use, store rates locally rather than fetching on every query:
-- Create a persistent rates tableCREATE TABLE exchange_rates ASSELECT * FROM ecb_exchange_rates();
-- Check what we haveSELECT COUNT(*) as total_rates, MIN(valid_from) as oldest, MAX(valid_from) as newestFROM exchange_rates;Update Rates Daily
Section titled “Update Rates Daily”-- Replace with fresh ratesCREATE OR REPLACE TABLE exchange_rates ASSELECT * FROM ecb_exchange_rates();
-- Or append historical dataINSERT INTO exchange_ratesSELECT * FROM ecb_exchange_rates()WHERE valid_from > (SELECT MAX(valid_from) FROM exchange_rates);Multi-Source Rates Table
Section titled “Multi-Source Rates Table”Combine multiple sources for better coverage:
CREATE TABLE exchange_rates AS-- ECB for EUR-based ratesSELECT * FROM ecb_exchange_rates()UNION ALL-- Bank of Canada for CAD ratesSELECT * FROM boc_exchange_rates()UNION ALL-- RBA for AUD ratesSELECT * FROM rba_exchange_rates();Schema
Section titled “Schema”All exchange rate functions return the same schema:
| Column | Type | Description |
|---|---|---|
| currency_from | ENUM | Source currency (ISO 4217) |
| currency_to | ENUM | Target currency (ISO 4217) |
| rate | DECIMAL(20,10) | Exchange rate |
| valid_from | TIMESTAMPTZ | Rate publication timestamp |
| valid_to | TIMESTAMPTZ | Rate expiry (NULL if current) |
| source | ENUM | Data source identifier |
| source_id | VARCHAR | Source-specific identifier |
| fetched_at | TIMESTAMPTZ | When data was fetched |
Free Sources (No API Key)
Section titled “Free Sources (No API Key)”ECB - European Central Bank
Section titled “ECB - European Central Bank”The European Central Bank publishes daily reference rates for 30+ currencies.
SELECT currency_from, currency_to, rate, valid_fromFROM ecb_exchange_rates()WHERE currency_to IN ('USD', 'GBP', 'JPY', 'CHF');- Base: EUR
- Update: Daily ~16:00 CET (weekdays)
- Currencies: USD, GBP, JPY, CHF, SEK, NOK, DKK, PLN, CZK, HUF, RON, BGN, TRY, AUD, CAD, CNY, HKD, IDR, ILS, INR, KRW, MXN, MYR, NZD, PHP, SGD, THB, ZAR, BRL
Bank of Canada (BOC)
Section titled “Bank of Canada (BOC)”SELECT currency_to, rate, valid_fromFROM boc_exchange_rates()ORDER BY valid_from DESC;- Base: CAD
- Update: Daily ~16:30 ET
Reserve Bank of Australia (RBA)
Section titled “Reserve Bank of Australia (RBA)”SELECT * FROM rba_exchange_rates()WHERE currency_to = 'USD';- Base: AUD
- Update: Daily 4:00 PM AEST
- License: CC BY 4.0 (attribution required)
Norges Bank (Central Bank of Norway)
Section titled “Norges Bank (Central Bank of Norway)”SELECT * FROM norges_bank_exchange_rates();- Base: NOK
- Update: Daily ~16:00 CET
Czech National Bank (CNB)
Section titled “Czech National Bank (CNB)”SELECT * FROM cnb_exchange_rates();- Base: CZK
- Update: Daily ~14:30 CET
Swiss National Bank (SNB)
Section titled “Swiss National Bank (SNB)”SELECT * FROM snb_exchange_rates();- Base: CHF
- Update: Daily 11:00 Zurich
- Currencies: EUR, USD, GBP, JPY (limited set)
FreeGoldAPI - Historical Gold Prices
Section titled “FreeGoldAPI - Historical Gold Prices”768 years of gold price history!
-- Get recent gold pricesSELECT valid_from, rate as usd_per_oz, data_sourceFROM freegold_exchange_rates()WHERE data_source = 'yahoo_finance'ORDER BY valid_from DESCLIMIT 10;Filter by data source for different timeframes:
-- Daily Yahoo Finance (2025+)SELECT * FROM freegold_exchange_rates()WHERE data_source = 'yahoo_finance';
-- Monthly World Bank (1960-2024)SELECT * FROM freegold_exchange_rates()WHERE data_source = 'worldbank';
-- Annual historical (1257-1959)SELECT * FROM freegold_exchange_rates()WHERE data_source = 'historical';Paid Sources (API Key Required)
Section titled “Paid Sources (API Key Required)”Configure API keys using DuckDB secrets:
-- Option 1: Direct secretCREATE SECRET (TYPE monetary, SOURCE <source>, SECRET 'your-api-key');
-- Option 2: From environment variableCREATE SECRET (TYPE monetary, PROVIDER env, SOURCE <source>);Twelve Data
Section titled “Twelve Data”Real-time forex and crypto rates.
-- Set API key (get free key at twelvedata.com)CREATE SECRET (TYPE monetary, SOURCE twelvedata, SECRET 'your-key');
-- Or from TWELVEDATA_API_KEY env varCREATE SECRET (TYPE monetary, PROVIDER env, SOURCE twelvedata);
-- Fetch ratesSELECT * FROM twelvedata_exchange_rates();- Base: USD
- Update: Real-time
- Free tier: 800 API calls/day
- Includes: Forex, crypto (BTC, ETH)
Open Exchange Rates (OXR)
Section titled “Open Exchange Rates (OXR)”CREATE SECRET (TYPE monetary, SOURCE openexchangerates, SECRET 'your-app-id');SELECT * FROM openexchangerates_exchange_rates();- Base: USD (free tier), configurable (paid)
- Update: Hourly
- Free tier: 1,000 requests/month
GoldAPI
Section titled “GoldAPI”Real-time precious metal prices.
CREATE SECRET (TYPE monetary, SOURCE goldapi, SECRET 'your-token');SELECT * FROM goldapi_exchange_rates();- Returns: XAU/USD, XAG/USD
- Update: Real-time
Practical Examples
Section titled “Practical Examples”Find Best Rate for a Currency Pair
Section titled “Find Best Rate for a Currency Pair”WITH all_rates AS ( SELECT 'ECB' as provider, * FROM ecb_exchange_rates() UNION ALL SELECT 'BOC' as provider, * FROM boc_exchange_rates())SELECT provider, currency_from, currency_to, rate, valid_fromFROM all_ratesWHERE currency_to = 'USD'ORDER BY valid_from DESCLIMIT 5;Track Rate Changes Over Time
Section titled “Track Rate Changes Over Time”-- Store historical ratesCREATE TABLE rate_history ASSELECT *, current_timestamp as snapshot_timeFROM ecb_exchange_rates();
-- Later, append new snapshotINSERT INTO rate_historySELECT *, current_timestamp as snapshot_timeFROM ecb_exchange_rates();
-- Compare EUR/USD over timeSELECT snapshot_time, rateFROM rate_historyWHERE currency_from = 'EUR' AND currency_to = 'USD'ORDER BY snapshot_time;Export Rates to Parquet
Section titled “Export Rates to Parquet”COPY (SELECT * FROM ecb_exchange_rates())TO 'ecb_rates.parquet' (FORMAT PARQUET);Summary Table
Section titled “Summary Table”| Function | Provider | API Key | Base | Update |
|---|---|---|---|---|
ecb_exchange_rates() | ECB | No | EUR | Daily |
boc_exchange_rates() | Bank of Canada | No | CAD | Daily |
rba_exchange_rates() | RBA | No | AUD | Daily |
norges_bank_exchange_rates() | Norges Bank | No | NOK | Daily |
cnb_exchange_rates() | CNB | No | CZK | Daily |
snb_exchange_rates() | SNB | No | CHF | Daily |
freegold_exchange_rates() | FreeGoldAPI | No | USD | Daily |
twelvedata_exchange_rates() | Twelve Data | Yes | USD | Real-time |
openexchangerates_exchange_rates() | OXR | Yes | USD | Hourly |
goldapi_exchange_rates() | GoldAPI | Yes | USD | Real-time |