How it works
Three steps from install to queryable transactions in Postgres.
Install the SheetLink CLI
A Node.js tool (Node 18 or later) for macOS, Linux and Windows.
npm install -g sheetlinkAuthenticate
Sign in through your browser, or save a Max API key so scheduled runs never need a browser. Create keys in your SheetLink dashboard.
sheetlink auth # browser sign-in, lasts 4 hours
sheetlink auth --api-key sl_... # Max API key, for unattended runsRun the sync
One command writes your transactions to sheetlink_transactions and your accounts and balances to sheetlink_accounts. The CLI creates both tables on the first run and upserts on later runs, so there are no duplicates. Postgres output is part of Max.
sheetlink sync --output postgres://user:pass@host/dbPostgres table schema
SheetLink creates these tables on the first sync. The schema is stable across syncs, so your downstream SQL stays intact. The transactions table has the same 34 columns SheetLink writes to Google Sheets and Excel, plus category (Plaid’s older category list).
CREATE TABLE IF NOT EXISTS sheetlink_transactions (
transaction_id TEXT PRIMARY KEY,
account_id TEXT,
persistent_account_id TEXT,
account_name TEXT,
account_mask TEXT,
date DATE NOT NULL,
authorized_date DATE,
datetime TEXT,
authorized_datetime TEXT,
description_raw TEXT,
merchant_name TEXT,
merchant_entity_id TEXT,
amount DECIMAL(10,2),
iso_currency_code TEXT,
unofficial_currency_code TEXT,
pending BOOLEAN,
pending_transaction_id TEXT,
check_number TEXT,
category_primary TEXT,
category_detailed TEXT,
payment_channel TEXT,
transaction_type TEXT,
transaction_code TEXT,
location_address TEXT,
location_city TEXT,
location_region TEXT,
location_postal_code TEXT,
location_country TEXT,
location_lat DECIMAL(10,7),
location_lon DECIMAL(10,7),
website TEXT,
logo_url TEXT,
source_institution TEXT,
category TEXT,
synced_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sheetlink_accounts (
account_id TEXT PRIMARY KEY,
persistent_account_id TEXT,
name TEXT,
official_name TEXT,
mask TEXT,
type TEXT,
subtype TEXT,
current_balance DECIMAL(10,2),
available_balance DECIMAL(10,2),
iso_currency_code TEXT,
institution TEXT,
last_synced_at TIMESTAMP DEFAULT NOW()
);amount follows Plaid’s sign: positive is money out (purchases, fees), negative is money in (deposits, refunds). The CLI doesn’t create indexes, so add your own if you query by date or account:
CREATE INDEX IF NOT EXISTS idx_sheetlink_transactions_date
ON sheetlink_transactions (date DESC);
CREATE INDEX IF NOT EXISTS idx_sheetlink_transactions_account_id
ON sheetlink_transactions (account_id);SheetLink uses INSERT ... ON CONFLICT (transaction_id) DO UPDATE, so running sheetlink sync again is safe and idempotent.
What to do with transactions in Postgres
BI tools (Metabase, Tableau, Superset)
Connect your BI tool directly to Postgres. Build dashboards showing monthly spend by category, cash flow over time or top merchants, all from your real bank data.
dbt models
Use the transactions table as a source in your dbt project. Write models to categorize spend, calculate rolling averages or join against other tables. The stable schema keeps your refs valid across syncs.
Custom analytics
Write SQL directly against the transactions table. Group by merchant, filter by date range, or work out account balances on any given day. No spreadsheet required.
Multi-user dashboards
If you manage finances for several entities (business units, clients, family members), each can sync into the same tables, told apart by account_name and source_institution, or into its own database on the same Postgres server.
Manual CSV vs. the SheetLink CLI
| Task | Manual CSV to Postgres | SheetLink CLI |
|---|---|---|
| Connect bank | Log in to each bank’s website | Once, through Plaid |
| Export data | Download a CSV from each bank | sheetlink sync --output postgres://... |
| Handle multiple accounts | A separate CSV per account | All accounts in one command |
| Schema consistency | Different columns per bank | Same schema every time |
| Deduplication | By hand: check for duplicate rows | Automatic, by upserting on transaction_id |
| Repeat monthly | The full manual process again | Run the same command again |
| Automation | Custom scripting required | Add it to cron or CI with a Max API key |
Pricing
| Plan | Price | What you get |
|---|---|---|
| Free | $0 | 1 bank, your last 7 days, Google Sheets |
| Pro | $4.99/mo or $39.99/yr | Every bank you connect, up to 2 years of history, Google Sheets and Excel, priority support |
| Max | $10.99/mo or $99/yr | Everything in Pro, plus Postgres and SQLite output, CLI automation (API keys), investments, and Claude |
Postgres output is part of Max. Compare every plan ›
Questions
How do I sync bank transactions to Postgres?
Install the SheetLink CLI with npm install -g sheetlink, authenticate with sheetlink auth (or save a Max API key with sheetlink auth --api-key for scheduled runs), then run sheetlink sync --output postgres://user:pass@host/db. The CLI creates the sheetlink_transactions and sheetlink_accounts tables automatically and upserts on transaction_id. Postgres output is part of Max.
What Postgres schema does SheetLink create?
A sheetlink_transactions table with 35 columns: the 34 columns SheetLink writes to Google Sheets and Excel (transaction_id as the upsert key, date, authorized_date, description_raw, merchant_name, amount, category_primary, category_detailed, all the location fields and more), plus category. It also creates a sheetlink_accounts table with each account’s name, mask, type, current and available balances, and institution.
Does SheetLink deduplicate transactions on each sync?
Yes. SheetLink uses transaction_id as a unique key and upserts (INSERT ... ON CONFLICT DO UPDATE). Running sync multiple times is safe: no duplicate rows are created.
What connection string format does SheetLink expect?
Pass the full connection string to the --output flag: sheetlink sync --output postgresql://user:password@host:5432/dbname (postgres:// works too). You can also pass $DATABASE_URL if that environment variable is set. SSL connections (Supabase, RDS and others) are configured with sslmode in the connection string.
Can I use SheetLink with a hosted Postgres service?
Yes. SheetLink works with any Postgres connection string, including Supabase, Neon, RDS, Heroku Postgres, PlanetScale (Postgres) and self-hosted instances.
What plan do I need for Postgres sync?
Postgres output is part of Max, at $10.99 a month or $99 a year. Max also includes SQLite output, CLI automation (API keys for scheduled runs), investments and Claude.