Docs menu

Docs

CLI reference

The sheetlink CLI lets you sync bank transactions from the command line, pipe JSON to other tools, and automate syncs via cron. Automation (API keys) and Postgres/SQLite output are Max features.

Overview

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.

Installation

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.6.0

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

Authentication

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

OAuth (Pro + Max)

Interactive

Opens your browser for Google sign-in. The resulting JWT is saved to ~/.sheetlink/config.json and used for subsequent commands. The login lasts 4 hours.

sheetlink auth

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

Not suitable for cron: Logins expire 4 hours after sign-in. If your cron job runs after the login expires, it will fail. Use an API key for cron automation.

API key (Max only)

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_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_your_key_here
sheetlink sync

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

Managing API KeysMax

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

1

Open the SheetLink Dashboard

Go to sheetlink.app/dashboard and sign in.

2

Navigate to API Keys

In the dashboard sidebar, click API Keys.

3

Create a new key

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

4

Revoke a key

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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. Keep them secret and treat them like passwords.

sheetlink sync

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
--from <date>YYYY-MM-DDStart of a custom date range, up to two years back
--to <date>YYYY-MM-DDEnd of a custom date rangetoday
--slimbooleanWrite the older 14-column set instead of the full schemaoff

Examples

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, to finance.db in the current folder Max

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

Sync one bank only

sheetlink sync --item VBX93wmRY4Iy...

A custom date range

sheetlink sync --from 2026-01-01 --to 2026-03-31

sheetlink items

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:

Connected banks (2):

  Chase
    item_id:      VBX93wmRY4Iy3kPqD7z8mN
    last synced:  10/2/2026, 8:14:03 AM
    accounts:
      ✓ Total Checking ••4821  checking
      ✓ Sapphire Preferred ••1937  credit card

  Ally Bank
    item_id:      KMN12xyzAB34cDeFGh5iJkL
    last synced:  10/2/2026, 8:14:05 AM
    accounts:
      ✓ Online Savings ••0458  savings
      ✗ Joint Checking ••7713  checking  (not syncing)

  ✗ = excluded from syncing. Manage which accounts sync in the SheetLink
    extension, web dashboard (sheetlink.app/dashboard/banks), or Excel add-in.

sheetlink auth

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.

Browser OAuth (interactive)

sheetlink auth

Save API key

sheetlink auth --api-key sl_your_key_here

Sign out by deleting the saved credentials

rm ~/.sheetlink/config.json

sheetlink config

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

Config keys

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)none

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

Output formats

JSON (default)

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, shortened (each transaction also carries Plaid’s other fields, such as authorized_date, location, website and logo_url). Amounts follow Plaid’s sign: money out is positive.

{
  "synced_at": "2026-04-09T08:00:00.000Z",
  "items": [
    {
      "item_id": "VBX93wmRY4Iy...",
      "accounts": [
        {
          "account_id": "acc_abc123",
          "name": "Chase Checking",
          "official_name": "Chase Total Checking",
          "mask": "4242",
          "type": "depository",
          "subtype": "checking",
          "current_balance": 2847.12,
          "available_balance": 2647.12,
          "institution": "Chase"
        }
      ],
      "transactions": [
        {
          "transaction_id": "txn_xyz789",
          "account_id": "acc_abc123",
          "account_name": "Chase Checking",
          "account_mask": "4242",
          "date": "2026-04-08",
          "description_raw": "WHOLE FOODS MARKET #123",
          "merchant_name": "Whole Foods Market",
          "amount": 47.23,
          "iso_currency_code": "USD",
          "pending": false,
          "payment_channel": "in store",
          "personal_finance_category": {
            "primary": "FOOD_AND_DRINK",
            "detailed": "FOOD_AND_DRINK_GROCERIES"
          },
          "source_institution": "Chase"
        }
      ],
      "tier": "max",
      "days_available": 730
    }
  ]
}

Pipe examples:

# Every merchant name
sheetlink sync | jq '[.items[].transactions[].merchant_name]'

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

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

CSV

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.

CSV columns, in order. Pass --slim for the older 14-column set.

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

PostgresMax

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

Auto-created schema:

-- Transactions: the 34 Google Sheets columns, plus category (Plaid's legacy 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()
);

-- Accounts
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()
);

Pass --slim to create the older 14-column transactions table instead (date, name, amount, category, account_id, account_name, account_mask, source_institution, pending, payment_channel, merchant_name, category_primary, category_detailed, transaction_id).

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).

SQLiteMax

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

sheetlink sync --output sqlite:///Users/you/finance.db

The file is created if it doesn’t exist, with the same sheetlink_transactions and sheetlink_accounts tables and columns as Postgres, in SQLite’s types (dates are stored as text).

Everything after sqlite:// is the file path, so three slashes give a full path and two give one from the current folder (sqlite://finance.db). From CLI 0.6.1, ~ works for your home folder too (sqlite:///~/finance.db).

Environment variables

Environment variables override values in ~/.sheetlink/config.json and 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_your_key_here
export SHEETLINK_OUTPUT=postgres://user:pass@host/mydb

# Then sync is just:
sheetlink sync

Cron & AutomationMax

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 4 hours. A cron job running after expiry will fail with a login-expired error. Always use a Max API key for cron.

Crontab example

Run crontab -e and add:

# Sync bank transactions to Postgres every day at 8am
0 8 * * * SHEETLINK_API_KEY=sl_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_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_your_key_here sheetlink sync --output csv --file /data/transactions/latest.csv 2>&1

Docker / CI example

# In your Dockerfile or CI config, pass the key as an env var
docker run --rm \
  -e SHEETLINK_API_KEY=sl_your_key_here \
  -e SHEETLINK_OUTPUT=postgres://user:pass@host/db \
  node:20-alpine \
  sh -c "npx sheetlink sync"

Logging

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

Quick start checklist for cron

  • Get Max at sheetlink.app/pricing
  • Create an API key in the dashboard (Dashboard → API Keys → New API Key)
  • Verify the key works: SHEETLINK_API_KEY=sl_... sheetlink sync
  • Set up your target database (Postgres) or confirm the CSV path is writable
  • Add the crontab entry
  • Monitor ~/sheetlink.log after the first run