SheetLink

CLI Reference

MAX

The sheetlinkCLI lets you sync bank transactions from the command line, pipe JSON to other tools, and automate syncs via cron. It’s a MAX-tier feature.

The CLI is an npm package that communicates with the SheetLink API. It’s designed for three primary use cases:

  • Cron automation: Run unattended nightly syncs that write to Postgres or SQLite without any human involvement. MAX API keys never expire, making them ideal for cron jobs.
  • ETL pipelines: Pipe the JSON output into jq, pandas, or any data tool. SheetLink becomes a read-only bank data source in your pipeline.
  • CSV snapshots: Download a flat CSV of all your transactions on demand. Great for importing into other tools or archiving.

Install the sheetlink package globally via npm or your preferred package manager:

npm

npm install -g sheetlink

yarn

yarn global add sheetlink

pnpm

pnpm add -g sheetlink

Verify the installation:

$ sheetlink --version
0.1.15

Node.js requirement: The CLI requires Node.js 18 or later. Check your version with node --version.

The CLI supports two authentication methods. Which one to use depends on your use case.

Interactive

Opens your browser for Google sign-in. The resulting JWT is saved to ~/.sheetlink/config.json and used for subsequent commands. JWTs expire after approximately 1 hour.

sheetlink auth

Best for: interactive use, one-off commands, development workflows.

Not suitable for cron: JWTs expire ~1 hour after sign-in. If your cron job runs after the JWT expires, it will fail. Use an API key for cron automation.

MAXNever expires

API keys are long-lived credentials that never expire. They’re ideal for automation. Pass the key via the --api-key flag or set the SHEETLINK_API_KEY environment variable.

sheetlink auth --api-key sl_live_your_key_here

This saves the key to ~/.sheetlink/config.json. After this, all commands use the API key automatically.

Security tip

Avoid passing --api-key directly in commands that end up in your shell history. Instead, use the SHEETLINK_API_KEY environment variable:

export SHEETLINK_API_KEY=sl_live_your_key_here
sheetlink sync

Or for cron, set it inline in the crontab (see Cron & Automation).

API keys are generated in the SheetLink dashboard. You can create multiple keys (e.g., one per automation script) and revoke them individually.

Go to sheetlink.app/dashboard and sign in.

In the dashboard sidebar, click API Keys.

Click New API Key, give it a descriptive name (e.g., “cron-nightly”), and copy the key immediately — it is only shown once.

Click the trash icon next to any key to permanently revoke it. Any automation using that key will immediately stop working.

API keys look like: sl_live_xxxxxxxxxxxxxxxxxxxxxxxx. Keep them secret — treat them like passwords.

The primary command. Fetches all transactions from your connected banks and outputs them in the format you specify.

FlagTypeDescriptionDefault
--output <dest>stringOutput destination. One of: json, csv, postgres://<connstr>, sqlite:///<path>json
--file <path>stringFile path when using --output csv. If omitted, writes to ./sheetlink-transactions.csv./sheetlink-transactions.csv
--item <item_id>stringSync only the specified bank item (use sheetlink items to find item_id values)all items

JSON to stdout (default)

sheetlink sync

Count transactions with jq

sheetlink sync | jq '.items[].transactions | length'

CSV snapshot to default path

sheetlink sync --output csv

CSV to custom path

sheetlink sync --output csv --file ~/finances.csv

Postgres upsert MAX

sheetlink sync --output postgres://localhost/mydb

SQLite upsert MAX

sheetlink sync --output sqlite:///~/finance.db

Sync one bank only

sheetlink sync --item VBX93wmRY4Iy...

Lists all bank items (connected institutions) on your account. Use this to find item_id values for use with sheetlink sync --item.

sheetlink items

Example output:

item_id                  institution
─────────────────────────────────────────────────
VBX93wmRY4Iy3kPqD7z8mN   Chase
KMN12xyzAB34cDeFGh5iJkL  Bank of America
PLQ98pqrST56uVwXYz7aBC9  Wells Fargo

Authenticates the CLI. Without flags, opens a browser for Google OAuth. With --api-key, saves an API key instead.

FlagTypeDescription
--api-key <key>stringSave a MAX API key to config. Skips browser OAuth flow.
--logoutbooleanRemove stored credentials from config file.

Browser OAuth (interactive)

sheetlink auth

Save API key

sheetlink auth --api-key sl_live_your_key_here

Sign out

sheetlink auth --logout

View and set persistent configuration values. Config is stored at ~/.sheetlink/config.json.

Show current config

sheetlink config

Set default output format

sheetlink config --set default_output=csv

Set default output to Postgres

sheetlink config --set default_output=postgres://user:pass@host/dbname

Change API URL (advanced)

sheetlink config --set api_url=https://api.sheetlink.app
KeyDescriptionDefault
default_outputDefault output format when --output is not specifiedjson
api_urlSheetLink backend URLhttps://api.sheetlink.app
api_keyMAX API key (set via sheetlink auth --api-key)

The config file is plain JSON at ~/.sheetlink/config.json. You can edit it directly if needed.

The default output. A structured JSON object is written to stdout, making it composable with any Unix tool. Nothing is written to disk unless you redirect the output.

Output shape:

{
  "synced_at": "2026-04-09T08:00:00.000Z",
  "items": [
    {
      "item_id": "VBX93wmRY4Iy...",
      "institution_name": "Chase",
      "accounts": [
        {
          "account_id": "acc_abc123",
          "name": "Chase Checking",
          "mask": "4242",
          "type": "depository",
          "subtype": "checking",
          "balances": {
            "current": 2847.12,
            "available": 2647.12
          }
        }
      ],
      "transactions": [
        {
          "transaction_id": "txn_xyz789",
          "date": "2026-04-08",
          "name": "Whole Foods Market",
          "amount": 47.23,
          "category": "Food and Drink",
          "account_id": "acc_abc123",
          "account_name": "Chase Checking",
          "account_mask": "4242",
          "institution_name": "Chase",
          "pending": false,
          "payment_channel": "in store",
          "merchant_name": "Whole Foods Market",
          "primary_category": "FOOD_AND_DRINK",
          "detailed_category": "FOOD_AND_DRINK_GROCERY_STORES"
        }
      ]
    }
  ]
}

Pipe examples:

# Get all transaction names
sheetlink sync | jq '[.items[].transactions[].name]'

# Sum all transaction amounts
sheetlink sync | jq '[.items[].transactions[].amount] | add'

# Filter by merchant
sheetlink sync | jq '.items[].transactions[] | select(.merchant_name == "Amazon")'

Writes a flat CSV file. Each sync overwrites the file — it’s a point-in-time snapshot, not an append log. The default output path is ./sheetlink-transactions.csv.

transaction_iddatenameamountcategoryaccount_idaccount_nameaccount_maskinstitution_namependingpayment_channelmerchant_nameprimary_categorydetailed_category
sheetlink sync --output csv --file ~/finances.csv

Upserts transactions and accounts into a Postgres database. Tables are created automatically on first run. Safe to run repeatedly — deduplication is handled by transaction_id and account_id.

sheetlink sync --output postgres://user:password@host:5432/dbname
-- Transactions table
CREATE TABLE IF NOT EXISTS sheetlink_transactions (
  transaction_id    TEXT PRIMARY KEY,
  date              DATE NOT NULL,
  name              TEXT,
  amount            NUMERIC(12, 2),
  category          TEXT,
  account_id        TEXT,
  account_name      TEXT,
  account_mask      TEXT,
  institution_name  TEXT,
  pending           BOOLEAN,
  payment_channel   TEXT,
  merchant_name     TEXT,
  primary_category  TEXT,
  detailed_category TEXT,
  synced_at         TIMESTAMPTZ DEFAULT NOW()
);

-- Accounts table
CREATE TABLE IF NOT EXISTS sheetlink_accounts (
  account_id       TEXT PRIMARY KEY,
  name             TEXT,
  mask             TEXT,
  type             TEXT,
  subtype          TEXT,
  institution_name TEXT,
  balance_current  NUMERIC(12, 2),
  balance_available NUMERIC(12, 2),
  synced_at        TIMESTAMPTZ DEFAULT NOW()
);

Upsert behavior: SheetLink uses INSERT ... ON CONFLICT DO UPDATE so rows are safely overwritten if transaction data changes (e.g., a pending transaction settles and the amount changes).

Same schema as Postgres, but written to a local SQLite file. No server required — great for local development, personal finance dashboards, or lightweight automation.

sheetlink sync --output sqlite:///~/finance.db

The file is created if it doesn’t exist. The same sheetlink_transactions and sheetlink_accounts tables are created with identical schema.

Environment variables override values in ~/.sheetlink/config.jsonand are useful for CI/CD, Docker, and cron environments where you don’t want to rely on a home-directory config file.

VariableDescription
SHEETLINK_API_KEYMAXAPI key for authentication. Overrides api_key in config file. Use this instead of --api-key to keep keys out of shell history.
SHEETLINK_OUTPUTDefault output format. Overrides default_output in config file. Accepts same values as --output.
SHEETLINK_API_URLSheetLink API base URL. Overrides api_url in config file. Useful for self-hosted setups.

Using env vars in practice:

# Set once in your shell profile
export SHEETLINK_API_KEY=sl_live_your_key_here
export SHEETLINK_OUTPUT=postgres://user:pass@host/mydb

# Then sync is just:
sheetlink sync

The most powerful SheetLink use case: an unattended nightly sync that keeps your database or CSV always up to date. This requires a MAX plan because cron jobs need credentials that never expire — API keys, not JWTs.

Important: OAuth JWTs from sheetlink auth expire after approximately 1 hour. A cron job running after expiry will fail with an authentication error. Always use a MAX API key for cron.

Run crontab -e and add:

# Sync bank transactions to Postgres every day at 8am
0 8 * * * SHEETLINK_API_KEY=sl_live_your_key_here sheetlink sync --output postgres://user:pass@host/db >> ~/sheetlink.log 2>&1

Sync to SQLite at 6am daily

0 6 * * * SHEETLINK_API_KEY=sl_live_your_key_here sheetlink sync --output sqlite:///home/user/finance.db >> ~/sheetlink.log 2>&1

Sync to CSV every hour

0 * * * * SHEETLINK_API_KEY=sl_live_your_key_here sheetlink sync --output csv --file /data/transactions/latest.csv 2>&1
# In your Dockerfile or CI config, pass the key as an env var
docker run --rm \
  -e SHEETLINK_API_KEY=sl_live_your_key_here \
  -e SHEETLINK_OUTPUT=postgres://user:pass@host/db \
  node:20-alpine \
  sh -c "npx sheetlink sync"

The CLI writes status messages to stderr and data to stdout. Redirect both for complete logs:

# Capture both stdout (data) and stderr (logs)
sheetlink sync --output postgres://... >> ~/sheetlink-data.log 2>> ~/sheetlink-errors.log

# Or combine both into one file
sheetlink sync --output postgres://... >> ~/sheetlink.log 2>&1