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 sheetlinkyarn
yarn global add sheetlinkpnpm
pnpm add -g sheetlinkVerify the installation:
$ sheetlink --version
0.6.0Node.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)
InteractiveOpens 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 authBest 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 expiresAPI 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_hereThis 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 syncOr 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.
Open the SheetLink Dashboard
Go to sheetlink.app/dashboard and sign in.
Navigate to API Keys
In the dashboard sidebar, click API Keys.
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.
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.
| Flag | Type | Description | Default |
|---|---|---|---|
--output <dest> | string | Output destination. One of: json, csv, postgres://<connstr>, sqlite:///<path> | json |
--file <path> | string | File path when using --output csv. If omitted, writes to ./sheetlink-transactions.csv | ./sheetlink-transactions.csv |
--item <item_id> | string | Sync only the specified bank item (use sheetlink items to find item_id values) | all items |
--from <date> | YYYY-MM-DD | Start of a custom date range, up to two years back | |
--to <date> | YYYY-MM-DD | End of a custom date range | today |
--slim | boolean | Write the older 14-column set instead of the full schema | off |
Examples
JSON to stdout (default)
sheetlink syncCount transactions with jq
sheetlink sync | jq '.items[].transactions | length'CSV snapshot to default path
sheetlink sync --output csvCSV to custom path
sheetlink sync --output csv --file ~/finances.csvPostgres upsert Max
sheetlink sync --output postgres://localhost/mydbSQLite upsert, to finance.db in the current folder Max
sheetlink sync --output sqlite://finance.dbSync one bank only
sheetlink sync --item VBX93wmRY4Iy...A custom date range
sheetlink sync --from 2026-01-01 --to 2026-03-31sheetlink items
Lists all bank items (connected institutions) on your account. Use this to find item_id values for use with sheetlink sync --item.
sheetlink itemsExample 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.
| Flag | Type | Description |
|---|---|---|
--api-key <key> | string | Save a Max API key to config. Skips browser OAuth flow. |
Browser OAuth (interactive)
sheetlink authSave API key
sheetlink auth --api-key sl_your_key_hereSign out by deleting the saved credentials
rm ~/.sheetlink/config.jsonsheetlink config
View and set persistent configuration values. Config is stored at ~/.sheetlink/config.json.
Show current config
sheetlink configSet default output format
sheetlink config --set default_output=csvSet default output to Postgres
sheetlink config --set default_output=postgres://user:pass@host/dbnameChange API URL (advanced)
sheetlink config --set api_url=https://api.sheetlink.appConfig keys
| Key | Description | Default |
|---|---|---|
default_output | Default output format when --output is not specified | json |
api_url | SheetLink backend URL | https://api.sheetlink.app |
api_key | Max 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.
sheetlink sync --output csv --file ~/finances.csvPostgresMax
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/dbnameAuto-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.dbThe 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.
| Variable | Description |
|---|---|
SHEETLINK_API_KEYMax | API key for authentication. Overrides api_key in config file. Use this instead of --api-key to keep keys out of shell history. |
SHEETLINK_OUTPUT | Default output format. Overrides default_output in config file. Accepts same values as --output. |
SHEETLINK_API_URL | SheetLink 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 syncCron & 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>&1Sync 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>&1Sync to CSV every hour
0 * * * * SHEETLINK_API_KEY=sl_your_key_here sheetlink sync --output csv --file /data/transactions/latest.csv 2>&1Docker / 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>&1Quick 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