Zero-dependency command-line client for the Frankfurter foreign-exchange API
Project description
exrate
A zero-dependency command-line client for the Frankfurter v2 foreign-exchange API. Daily reference rates from 84 central banks, 201 currencies, back to 1948. No API key, no login, no rate caps for normal use.
It's a single Python file (exrate.py, stdlib only) — runs anywhere Python 3.8+ exists.
Every subcommand maps 1:1 to the Frankfurter OpenAPI spec,
and every subcommand accepts --json for machine-readable output, which also makes
it pleasant to drive from scripts and AI agents.
Install
From PyPI (recommended):
pipx install exrate # isolated, puts `exrate` on your PATH
# or
pip install exrate
From source (symlink install for hacking on it):
git clone https://github.com/ekinertac/exrate.git
cd exrate
./install.sh # symlinks exrate.py -> ~/.local/bin/exrate
exrate --help
install.sh symlinks (not copies) the script, so edits take effect with no reinstall.
It installs into ~/.local/bin; make sure that's on your PATH.
Usage
exrate rate EUR USD # latest single pair
exrate rate EUR USD --date 2020-03-15 # historical
exrate convert 100 USD JPY # convert an amount (client-side)
exrate rates --base USD --quotes EUR,GBP,JPY # several pairs at once
exrate rates --from 2026-01-01 --quotes USD # daily time series
exrate rates --from 2026-01-01 --group month # downsample to monthly
exrate rates --from 2026-01-01 --quotes USD --csv # native CSV
exrate currencies # list currency codes
exrate currencies --all # include legacy currencies
exrate currency AED # one currency's details + peg
exrate providers # data sources (central banks)
Add --json to any command for raw API JSON (numbers stay JSON numbers, not strings),
handy for jq pipelines:
exrate currencies --json | jq -r '.[].iso_code'
exrate providers --json | jq -r '.[].key'
Run exrate <subcommand> --help for that command's modes, fields, and examples.
Commands
| Command | Endpoint | Purpose |
|---|---|---|
rate BASE QUOTE |
GET /rate/{base}/{quote} |
One pair, latest or historical |
rates |
GET /rates |
Many rates: latest, a date, or a time series |
convert AMOUNT FROM TO |
(client-side) | Multiply amount by the pair rate |
currencies |
GET /currencies |
List supported currency codes |
currency CODE |
GET /currency/{code} |
One currency's details + peg metadata |
providers |
GET /providers |
Data providers (central banks) and their keys |
Conventions
- Currency codes are ISO 4217, 3 letters, case-insensitive (
eur==EUR). - Dates are
YYYY-MM-DD. Weekends/holidays return the last published rate. - Default base is
EUR. The base is the "1 unit" side; the rate says how many quote units equal 1 base unit. - Providers: rates are blended across providers by default. Pass
--providers ECBto pin a single official source. - Conversion is client-side — the API has no conversion endpoint, so
convertfetches the pair rate and multiplies (matching the official docs' approach).
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
API or network error (message on stderr, e.g. unknown currency) |
2 |
Bad usage / invalid arguments |
Releasing
Versions are single-sourced from __version__ in exrate.py. To cut a release:
- Bump
__version__inexrate.py. - Commit and tag:
git tag v1.2.3 && git push --tags. - On GitHub: Releases → Draft a new release, pick the tag, Publish.
Publishing a GitHub Release triggers .github/workflows/publish.yml, which builds
the sdist + wheel and uploads them to PyPI via Trusted Publishing (OIDC, no
stored tokens). One-time PyPI setup before the first Actions run:
PyPI → Your projects / Publishing → Add a pending publisher · Owner:
ekinertac· Repository:exrate· Workflow:publish.yml· Environment:pypi
To build/publish manually instead:
pipx run build # -> dist/*.tar.gz, dist/*.whl
pipx run twine check dist/*
pipx run twine upload dist/* # prompts for a PyPI API token
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file exrate-1.0.0.tar.gz.
File metadata
- Download URL: exrate-1.0.0.tar.gz
- Upload date:
- Size: 9.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ed1dfe29caf4fbf6a89c82cc977eba4d4946014f435433fcc49e96996eec2f8
|
|
| MD5 |
6f768c11e2107b1ed654947a39e437e7
|
|
| BLAKE2b-256 |
3d12184f7602022234f55c241d2b0e1b005212e28b6aaed96eafae2b653ee701
|
File details
Details for the file exrate-1.0.0-py3-none-any.whl.
File metadata
- Download URL: exrate-1.0.0-py3-none-any.whl
- Upload date:
- Size: 10.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
406c87d3af625b7303c086ef21d49281f6c81e475c3eea7ab887fe8b2a9f9f88
|
|
| MD5 |
260cb60a8bd73ddac9613531e454c7f8
|
|
| BLAKE2b-256 |
65e1a2b5f5a49ae5d6b0c6da1fbdeb5af5c59987ba8551426d071d7dd4f53c1b
|