Skip to content

monetary - Type-Safe Money for DuckDB

Stop storing money as DECIMAL. Get currency-aware types, automatic parsing, and real-time exchange rates in your database.
GitHub stars70+ Currencies10 Exchange Rate Sources

Storing money as DECIMAL(10,2) causes real bugs:

-- Mixing currencies silently corrupts data
SELECT SUM(amount) FROM transactions; -- 💥 USD + EUR + JPY = ???
-- Japanese Yen has no decimals, Kuwaiti Dinar has 3
INSERT INTO prices VALUES (100.50, 'JPY'); -- Wrong! JPY has 0 decimals
-- Currency symbols are ambiguous
SELECT '$100'; -- USD? CAD? AUD? SGD?
DuckDB

          
DuckDB

          
INSTALL monetary FROM community;
LOAD monetary;

Type-Safe Arithmetic

Adding EUR + USD throws an error instead of silently corrupting your data. Catch currency bugs at query time, not in production.

Smart Parsing

Understands $99.99, €100, ¥10000, 100 USD, and USD 100. Rejects ambiguous symbols like S$ with helpful errors.

Correct Precision

JPY/KRW: 0 decimals. USD/EUR: 2 decimals. KWD/BHD: 3 decimals. BTC: 8 decimals. All handled automatically.

10 Rate Sources

ECB, Bank of Canada, RBA, Norges Bank, CNB, SNB, and more. 7 free sources, 3 paid for real-time data.

These examples run in your browser using DuckDB WASM:

DuckDB

          
DuckDB

          
DuckDB

          
-- 1. Load exchange rates (free, no API key)
CREATE TABLE exchange_rates AS
SELECT * FROM ecb_exchange_rates();
-- 2. Configure the extension
SET monetary_exchange_table = 'exchange_rates';
-- 3. Convert with arrow operator or macros
SELECT '100 EUR'::monetary ->> 'USD' AS arrow_syntax,
USD('100 EUR'::monetary) AS macro_syntax;
-- E-commerce: prices in local currency, reports in USD
CREATE TABLE orders (
id INTEGER,
customer_country VARCHAR,
amount monetary
);
INSERT INTO orders VALUES
(1, 'DE', '99.99 EUR'),
(2, 'JP', '15000 JPY'),
(3, 'GB', '79.99 GBP');
-- Convert everything to USD for reporting
-- Note: Requires exchange_rates table configured
SELECT id, amount, amount ->> 'USD' AS amount_usd
FROM orders;
-- Safe conversion with fallback for missing rates
SELECT id, amount,
TRY(amount ->> 'USD') AS amount_usd,
CASE WHEN TRY(amount ->> 'USD') IS NULL
THEN 'Rate unavailable' END AS status
FROM orders;
SourceAPI KeyBaseUpdateBest For
ECBFreeEURDailyEU businesses
Bank of CanadaFreeCADDailyCanadian businesses
RBAFreeAUDDailyAustralian businesses
Norges BankFreeNOKDailyNordic businesses
Czech National BankFreeCZKDailyCzech businesses
Swiss National BankFreeCHFDailySwiss businesses
FreeGoldAPIFreeXAUDailyPrecious metals
Twelve DataPaidAnyReal-timeTrading apps
Open Exchange RatesPaidUSDHourlyGlobal apps
GoldAPIPaidXAUReal-timePrecious metals trading
FeatureDECIMAL(10,2)monetary
Currency stored with value❌ Separate column✅ Built-in
Prevents mixing currencies❌ Silent corruption✅ Throws error
Correct decimals per currency❌ Manual✅ Automatic
Parse $99 or €100❌ Manual✅ Built-in
Exchange rate conversion❌ Manual joinsprice ->> 'USD'
Ambiguous symbol detection❌ No✅ Helpful errors

Use Cases

See real-world examples for e-commerce, finance, and crypto.

View Use Cases →

Double-Entry Ledgers

Build accounting systems with currency-validated transactions.

Ledger Patterns →