splitwise-fx
Bulk currency conversion for Splitwise expenses.
Pulls a Splitwise group's expenses, converts every foreign-currency line to a target currency at the UnionPay transaction-date rate (the rate Chinese banks actually used to settle), and writes the converted values back via the Splitwise API.
Install
uv tool install splitwise-fx
(Or pipx install splitwise-fx, or any other tool that installs Python CLIs into isolated venvs.)
After install, splitwise-fx is on your PATH.
Setup
Get a Splitwise API key from https://secure.splitwise.com/apps and export it:
export SPLITWISE_API_KEY=your-key-here
(Add to ~/.zshrc / ~/.bashrc to persist. You can also pass --api-key per-invocation, or drop a .env file in the directory you run from.)
Usage
# preview, then prompt y/N to apply
splitwise-fx --group "东京旅行" --to CNY
# preview only — exit without prompting
splitwise-fx --group "东京旅行" --to CNY --dry-run
# skip the prompt (automation)
splitwise-fx --group "东京旅行" --to CNY --yes
# date window
splitwise-fx --group "东京旅行" --to CNY --after 2026-04-01 --before 2026-05-01
# bypass the on-disk rate cache
splitwise-fx --group "东京旅行" --to CNY --no-cache
--group accepts either the group's name (case-insensitive) or its numeric ID. If the name is ambiguous or missing, the CLI lists matches and exits.
How it works
- Pulls expenses from the chosen group, paginating Splitwise's API.
- For each foreign-currency expense, looks up that day's rate.
- Skips settle-up payments and same-currency lines.
- Converts cost + each user's
paid_shareandowed_shareusingDecimal. Distributes ±0.01 rounding residuals to the largest non-zero shares sosum(paid) == sum(owed) == costholds (Splitwise rejects updates that violate this). - Renders a Rich preview with rate source per line (
UPIorFRA). - On confirm, posts each update via
POST /update_expense/{id}, sleeping 1 s between writes.
Rate sources
| Source | When used | Notes |
|---|---|---|
UnionPay (unionpayintl.com/upload/jfimg/{YYYYMMDD}.json) |
Target currency is one of: AUD CAD CNY EUR GBP HKD HUF JPY MNT MOP NZD SGD THB USD VND | Files exist for every day including weekends/CN holidays (Friday rate carries through). |
Frankfurter (api.frankfurter.dev/v2/rate/{src}/{dst}) |
Target outside UnionPay's 15 base currencies, or pair missing from UnionPay file | Free, ECB-backed. |
Rates are cached on disk under ${XDG_CACHE_HOME:-~/.cache}/splitwise-fx/. Past dates never expire (historical rates are immutable); today's date refreshes every hour.
Caveats
- Idempotency: re-running with a different
--toafter a first conversion will re-convert already-converted expenses. The filter only skips lines whosecurrency_codealready matches the target. - Settle-up payments are skipped — converting them would corrupt the ledger.
- Splitwise rate limits are undocumented; the CLI sleeps 1 s between writes and backs off on 429s.
Development
git clone https://github.com/whtsky/splitwise-fx
cd splitwise-fx
uv sync
# linters / type-checker / tests
uv run ruff check
uv run ruff format --check
uv run mypy src tests
uv run pytest
Releasing (maintainer notes)
Releases publish to PyPI via GitHub Actions Trusted Publishing on tag push:
# bump version in pyproject.toml and src/splitwise_fx/__init__.py
git commit -am "release: vX.Y.Z"
git tag vX.Y.Z
git push origin main vX.Y.Z
The release.yml workflow builds with uv build and publishes via pypa/gh-action-pypi-publish. Trusted publishing is configured one-time at https://pypi.org/manage/account/publishing/.
License
MIT — see LICENSE.
Metadata
Release files for splitwise-fx 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| splitwise_fx-0.0.1.tar.gz | 14.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| splitwise_fx-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.5 kB
Release files / splitwise_fx-0.0.1.tar.gz
| Download URL | splitwise_fx-0.0.1.tar.gz |
|---|---|
| Size | 14.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5060e2c631732ea3cfc5cc4e4f2d84e36b338c356d40e4009e408e0b1ffc6657
|
|
BLAKE2b-256 checksum How to use checksums |
e85de9f59d719940dbb818b9b30c62c8dbe8557a9a6e817e9a4bc78ed5e50cf8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 7, 2026.
Transparency logRelease files / splitwise_fx-0.0.1-py3-none-any.whl
| Download URL | splitwise_fx-0.0.1-py3-none-any.whl |
|---|---|
| Size | 18.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4e94eadbdcdefcbdc2a831cc17ac0a05694a99e8baad353ce8efe594995f4d8b
|
|
BLAKE2b-256 checksum How to use checksums |
a2ef5145020539e27d814996c37467d5fe1391103c14088945e87e13c2983f31
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 7, 2026.
Transparency log