Self-hosted open-source expense and budget tracker with balances, transfers, and multi-currency reporting on Postgres.
Live demo: expense-budget-tracker.com
- Fully open-source — all code is available, deploy on your own servers with full control over your data
- SQL Query API — generate an API key, give it to your LLM agent, and let it query, analyze, and manage your financial data via HTTP. Minimal, flat table structure designed to be hard to misuse — ideal for AI agents
- Budget and transaction UI — built-in interface for budgeting, browsing transactions, and tracking balances across accounts and currencies
git clone https://github.com/kirill-markin/expense-budget-tracker.git
cd expense-budget-tracker
open -a Docker # start Docker if not running (macOS)
make up # start Postgres, run migrations, start web + workerOpen http://localhost:3000.
Start at GET https://api.expense-budget-tracker.com/v1/. The discovery response tells agents to ask for the user's email first, and the same email OTP flow covers both signup and login.
- Open
GET https://api.expense-budget-tracker.com/v1/in your agent — it will discover the OTP onboarding flow automatically - Complete email OTP login — the auth service returns a long-lived
ApiKey - Give the key to your AI agent — Claude Code, Codex, or any agent that can call HTTP APIs
- Send the agent screenshots, CSV files, or PDF bank statements — it parses them and inserts transactions via the SQL API
- Open the web UI — view actual spending by category and plan the budget for the next month
Example request the agent sends:
curl -X POST https://api.expense-budget-tracker.com/v1/sql \
-H "Authorization: ApiKey ebta_..." \
-H "X-Workspace-Id: workspace-id" \
-H "Content-Type: application/json" \
-d '{"sql": "INSERT INTO ledger_entries (event_id, ts, account_id, amount, currency, kind, category, counterparty, note) VALUES ('"'"'evt-001'"'"', '"'"'2025-03-15 12:30:00+00'"'"', '"'"'chase-checking'"'"', -42.50, '"'"'USD'"'"', '"'"'spend'"'"', '"'"'groceries'"'"', '"'"'Whole Foods'"'"', '"'"'Weekly groceries'"'"')"}'After POST /v1/workspaces/{workspaceId}/select, the API key remembers that workspace, so X-Workspace-Id becomes optional on later /v1/sql calls. If the user has exactly one workspace and no saved selection yet, the API auto-saves and uses that single workspace.
-
Deployment — local Docker Compose and AWS CDK setup
-
AWS deployment — full AWS CDK guide
-
Architecture — system overview, data model, multi-currency design
-
For full privacy, self-host on your own AWS account. If you deploy or use the hosted service with the AWS/CDK setup described in
infra/aws/README.md, the LLM chat runtime stores transcript state in Postgres. That means chat data is available to:- the deployed service operator, because the data is stored in the service database
- OpenAI, because chat requests are sent there to power the feature
- Langfuse Cloud, because chat telemetry is exported there
If you use chat to import bank statements, screenshots, PDFs, CSVs, or other financial files, data extracted from those files can also reach OpenAI and Langfuse as part of chat processing and tracing. If you do not trust even one of these parties, do not store your financial data in this hosted deployment.
-
The code is already deployed, and the maintainer stores his own real finances there. Only maintainer Kirill Markin has access to the demo database. For partial privacy, sign up with an email that doesn't contain your real name. Try the demo →