Skip to content

Latest commit

 

History

15 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

itemized

Turn every Amazon Store Card charge into a receipt.

Your card statement says AMAZON.COM $47.03. Amazon splits one order into several charges (one per shipment) and never shows which items made up which charge. itemized scrapes your own Amazon account nightly, matches each card charge to the exact items behind it, and shows the result as a receipt — plus your current card balance, a running ledger of new charges this cycle, and a Telegram digest when the balance moves.

Self-hosted, single-user, LAN-only. Built for a homelab; no cloud services involved beyond your own Amazon session.

The charges timeline, with one charge opened into its receipt

Card page: balance, new charges this cycle, running ledger Statement view: one card per billing cycle
Card — your live balance, what the next statement will bill, and a running ledger where every step up is a charge. Statement — billing cycles as your card issuer draws them, each line expandable into its receipt.
Command palette searching item titles The same receipt on a phone
Search — ⌘K from anywhere: find an item, jump to the charge that paid for it. Mobile — the same receipt on a phone.

Screenshots use generated sample data, not a real account.

Setting it up takes about two minutes

The app walks you through it on first load — no config files to edit:

Step 1: choose a password for itemized itself Step 2: enter your Amazon sign-in
1. Protect the app — a password for itemized itself. Until one is set, the API refuses every request. 2. Your Amazon sign-in — stored on your machine so the scraper can re-authenticate when Amazon asks.

Then 3. Connect signs in for you — showing Amazon's page inline only if it asks to verify — and 4. First sync pulls your history. Install steps are below.

How it works

Amazon has no consumer API for orders. What it does have is a logged-in Transactions page (amazon.com/cpe/yourpayments/transactions) that lists every charge made to each of your cards with the order number it belongs to. itemized:

  1. Runs a headless Chromium (Playwright) with a persistent profile, bootstrapped once by pasting cookies exported from your browser (no password is ever stored).
  2. Reads the Transactions page newest-first until it reaches charges it already knows, then the details page of every new order (items, prices, tax, shipments, images), and the store card's My Card page (balance, due date, minimum due).
  3. Matches charges to items with a confidence ladder: single charge → per-shipment sums → cent-exact subset-sum → proportional. Gift-card and cashback-points payments are recognised and kept out of your card totals.
  4. Serves a React UI: charges timeline with unrolling receipts, statement view mirroring your card statement by billing cycle, item search (⌘K), the Card page (balance + running ledger), sync status, settings.

Syncing is two-tier: a light check (every 30 minutes by default) reads just the Transactions page — the session cookie alone is enough there, so it costs no sign-in and takes about a second — and the moment it spots a new charge it carries on into a full run: item details, card balance, matching, and a Telegram digest. A full run also happens nightly. Order and card pages are what make Amazon demand a fresh password, so they are never touched speculatively.

When Amazon eventually wants a verification step it cannot answer, you get a Telegram alert and finish it in the browser view; the next run picks up where it left off.

Stack

  • server/ — Node 24, TypeScript, Express, MySQL 8, Playwright, node-html-parser, node-cron. Pure HTML parsers with sanitized real-page fixtures, so an Amazon redesign fails tests loudly instead of corrupting data.
  • web/ — React 19, Vite, TypeScript, Tailwind v4, Framer Motion, TanStack Query, Radix.

Install on Linux (one command)

On a fresh Ubuntu 24.04 (or Debian 12) box with a normal user that can sudo:

git clone https://github.com/mbrande/amazon_itemized.git itemized
cd itemized
sudo scripts/install.sh

That installs Apache, MySQL 8, Node 24 and Chromium's libraries; creates the database and a user with a generated password; asks a few questions (which network may open the app, its address, optionally your Amazon sign-in — or leave that for the setup wizard); builds everything; installs a systemd service and an Apache site that serves /itemized/ and proxies the API, restricted to your LAN; then checks the whole path with a real request. It ends by printing the URL to open. Finish there: app password → Amazon sign-in → first sync.

Afterwards:

update git pull && npm run deploy
logs journalctl -u itemized -f
settings /etc/itemized.env (then sudo systemctl restart itemized)
change answers sudo scripts/install.sh --reconfigure
non-interactive sudo scripts/install.sh --yes (defaults; no Amazon credentials)
bring your own MySQL 8 sudo scripts/install.sh --existing-db
remove sudo scripts/install.sh --uninstall (asks before dropping the database)

Re-running the installer is safe; it reuses /etc/itemized.env and never rotates the database password on its own. The generated password and anything typed at a hidden prompt are written only to /etc/itemized.env (root:<you>, mode 0640) — never printed. MariaDB will not do: the schema uses MySQL 8 collations and JSON columns. Anything the installer doesn't cover (other distros, a reverse proxy other than Apache) — the manual steps below are what it automates.

Manual setup

npm install
npx playwright install chromium --workspace server   # or: cd server && npx playwright install chromium

Create a MySQL database + user, then an env file (systemd uses /etc/itemized.env; for dev, server/.env works too):

PORT=8772
DB_HOST=127.0.0.1  DB_USER=itemized  DB_PASS=…  DB_NAME=itemized
ITEMIZED_STATE_DIR=/home/you/.itemized      # browser profile + cookies, chmod 700
ITEMIZED_PUBLIC_URL=http://your-host/itemized # used in alert links
ITEMIZED_ALERT_WEBHOOK=                       # optional; also editable in Settings
TZ=America/Los_Angeles
AMAZON_EMAIL=you@example.com                  # optional: the setup wizard can store these instead
AMAZON_PASSWORD=…                             # read only at sign-in time; never logged or served
AMAZON_TOTP_SECRET=                           # optional base32 secret if your Amazon 2FA uses an authenticator app

Amazon asks for a fresh password on order and card pages roughly an hour after the last one. With AMAZON_EMAIL / AMAZON_PASSWORD set, the scraper answers that itself. Anything a script should not answer (a code sent to your phone, "verify it's you", a captcha) is relayed to Settings → Sign in to Amazon: a live view of the scraper's own browser you can click and type into, plus a Telegram alert when it is needed.

npm run migrate -w server      # apply migrations
npm run dev:server             # API on 127.0.0.1:8772
npm run dev:web                # Vite on 127.0.0.1:5173 (proxies /itemized/api)

Open the app and a setup wizard takes over on first run: it first asks you to set an app password (the API refuses every request without it — the app holds a signed-in Amazon browser and your entire purchase history, so it must not sit open on a network), then: enter your Amazon sign-in, press Connect (itemized signs in and shows you Amazon's page only if a verification step appears), then start the first backfill. Credentials entered there are written to <ITEMIZED_STATE_DIR>/credentials.env with owner-only permissions; the AMAZON_* environment variables above are an alternative for deployments that manage secrets outside the app and always take precedence. Pasting cookies exported with the Cookie-Editor extension still works as a fallback (Settings → Alternative).

Production on this repo's home box: Apache serves web/dist at /itemized/, proxies /itemized/api to the systemd service, and restricts both to the LAN (Require ip). scripts/deploy.sh builds, syncs and restarts. See CLAUDE.md for the full runtime layout and conventions.

Telegram alerts

n8n/itemized-alerts.json is an importable n8n workflow (Webhook → Telegram). Import it, set your chat ID and credential, activate, and paste the webhook URL into Settings. Payload: { app, level, title, message, url, at }.

Caveats, honestly

  • This is unofficial scraping of your own account. Amazon can change the pages at any time; the parser tests and the parse_failed guard exist for exactly that.
  • The store card's My Card page may demand a fresh password (Amazon's max_auth_age=3600 policy) after certain account actions; the balance step is best-effort and never fails a run. Never open the card's payment-history link from the scraper — it triggers a bank OTP challenge.
  • No app-level auth: it relies on network-level restriction. Do not expose it to the internet.
  • Never run npm run seed against a database holding real data — it wipes it (it exists for UI development).

License

MIT

About

Turn every Amazon Store Card charge into a receipt — matches card charges to the items behind them, tracks balance and billing cycles

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages