A small command-line tool that fetches foreign-exchange rates from the Frankfurter API and writes them to a tidy CSV. Built to be readable, well-tested, and easy to extend — the kind of small automation I deliver for clients.
- Fetch latest or historical rates for any base currency and a list of targets.
- Resilient HTTP: configurable timeout, retries, and linear backoff on
transient failures (network errors and
5xx); fast-fail on client errors (4xx). - Clear, typed errors (
RateFetchError/ApiError) and sensible exit codes. - Clean CSV output (
date, base, currency, rate). - Fully unit-tested without network access — the HTTP layer is injectable.
pip install -r requirements.txt# Latest USD rates for a few currencies
python ratefetch.py --base USD --symbols EUR,GBP,INR,JPY --out rates.csv
# A specific historical day
python ratefetch.py --base EUR --symbols USD,JPY --date 2026-06-20 --out eur.csv
# More retries + verbose logging
python ratefetch.py --base USD --symbols INR --retries 5 --verbosedate,base,currency,rate
2026-06-20,USD,EUR,0.920000
2026-06-20,USD,GBP,0.780000
2026-06-20,USD,INR,83.400000
2026-06-20,USD,JPY,157.200000| Flag | Default | Description |
|---|---|---|
--base |
USD |
Base currency (3-letter code) |
--symbols |
EUR,GBP,INR,JPY |
Comma-separated target currencies |
--date |
latest | YYYY-MM-DD for a historical day |
--out |
rates.csv |
Output CSV path |
--retries |
3 |
Extra attempts on transient errors |
--timeout |
10.0 |
Per-request timeout (seconds) |
-v, --verbose |
off | Debug logging |
python -m pytest -q
# 16 passedTests cover success parsing and sorting, input normalisation, missing-symbol
handling, 4xx fast-fail, 5xx/network retry-then-succeed, retry exhaustion,
malformed payloads, CSV output, argument parsing, and CLI exit codes.
fetch_rates()accepts an optionalrequests.Session, so the network layer can be mocked in tests and swapped for a pooled/authenticated session in production.- Retry policy is explicit and bounded; backoff is injectable so tests run instantly.
- The parser validates the response shape and currency codes up front and fails with actionable messages rather than raising deep in the stack.
MIT — use it however you like.