A TypeScript SDK for the Monarch API.
npm install monarch-apiimport { MonarchClient } from 'monarch-api';
const client = new MonarchClient();
await client.login({
email: 'you@example.com',
password: 'your-password',
});
const accounts = await client.accounts.list();
console.log(accounts);const client = new MonarchClient();
await client.login({
email: 'you@example.com',
password: 'your-password',
});If your account has MFA enabled, login will throw a RequireMFAError. Handle it by prompting the user for their code:
import { MonarchClient, RequireMFAError } from 'monarch-api';
const client = new MonarchClient();
try {
await client.login({ email, password });
} catch (err) {
if (err instanceof RequireMFAError) {
await client.multiFactorAuthenticate({
email,
password,
code: '123456', // 6-digit TOTP code
});
}
}If you have access to your TOTP secret key, you can pass it directly to login and the SDK will handle MFA automatically:
await client.login({
email,
password,
mfaSecretKey: 'YOUR_TOTP_SECRET',
});To avoid re-authenticating on every run, you can initialize the client with an existing token:
const client = new MonarchClient({ token: 'your-token' });For scripts and automations, we recommend saving your session token to a file (e.g. ~/.monarch-session.json) and reusing it across runs. This avoids logging in on every execution.
import { MonarchClient, RequireMFAError } from 'monarch-api';
import { readFileSync, writeFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
const SESSION_FILE = join(homedir(), '.monarch-session.json');
function loadSession(): { token: string } | null {
try {
return JSON.parse(readFileSync(SESSION_FILE, 'utf-8'));
} catch {
return null;
}
}
function saveSession(token: string) {
writeFileSync(SESSION_FILE, JSON.stringify({ token }), { mode: 0o600 });
}
const session = loadSession();
const client = new MonarchClient(session ?? undefined);
if (!session) {
try {
await client.login({ email, password });
} catch (err) {
if (err instanceof RequireMFAError) {
await client.multiFactorAuthenticate({ email, password, code: '123456' });
} else {
throw err;
}
}
saveSession(client.token!);
}
// client is ready to use
const accounts = await client.accounts.list();Note:
~/.monarch-session.jsoncontains a sensitive auth token. The example above sets file permissions to0o600(owner read/write only). Add it to your.gitignoreif the file is inside a project directory.
// List all connected accounts
const accounts = await client.accounts.list();
// Create a manual account
const { id } = await client.accounts.create({
name: 'Savings Account',
type: 'savings',
balance: 5000,
currency: 'USD',
});
// Update an account
const updated = await client.accounts.update(accountId, {
displayName: 'Emergency Fund',
includeInNetWorth: true,
});
// Delete an account
await client.accounts.delete(accountId);
// Get investment holdings
const holdings = await client.accounts.getHoldings(accountId);
// Get balance history
const history = await client.accounts.getHistory(accountId, {
startDate: '2025-01-01',
endDate: '2025-12-31',
});
// Refresh account data from institution
await client.accounts.refresh([accountId]);
// Refresh and wait for sync to complete
await client.accounts.refreshAndWait([accountId]);// List transactions
const transactions = await client.transactions.list({
startDate: '2025-01-01',
endDate: '2025-01-31',
limit: 100,
});
// Get a single transaction
const tx = await client.transactions.get(transactionId);
// Create a manual transaction
const { id } = await client.transactions.create({
accountId,
date: '2025-01-15',
amount: -42.50,
merchantName: 'Coffee Shop',
categoryId,
});
// Update a transaction
await client.transactions.update(transactionId, {
notes: 'Team lunch',
categoryId: 'food-and-dining',
});
// Delete a transaction
await client.transactions.delete(transactionId);
// Split a transaction
await client.transactions.updateSplits(transactionId, [
{ amount: 25.00, categoryId: 'groceries' },
{ amount: 17.50, categoryId: 'household' },
]);
// Categories
const categories = await client.transactions.categories.list();
const groups = await client.transactions.categories.groups();
await client.transactions.categories.create({ name: 'Pet Care', groupId });
// Tags
const tags = await client.transactions.tags.list();
await client.transactions.tags.create('Reimbursable', '#FF5733');
await client.transactions.tags.set(transactionId, [tagId]);// Get budget data for a month
const { budgetData, categoryGroups } = await client.budgets.list({
startDate: '2025-01-01',
endDate: '2025-01-31',
});
// Set a budget amount
await client.budgets.set(categoryId, 500, {
startDate: '2025-01-01',
endDate: '2025-01-31',
rolloverEnabled: true,
});// Get full cashflow breakdown
const cashflow = await client.cashflow.get({
startDate: '2025-01-01',
endDate: '2025-01-31',
});
// Get cashflow summary
const summary = await client.cashflow.getSummary({
startDate: '2025-01-01',
endDate: '2025-01-31',
});
// { income, expenses, savings, savingsRate }// List recurring transactions
const recurring = await client.recurring.list({
startDate: '2025-01-01',
endDate: '2025-01-31',
});
// Update a recurring transaction
await client.recurring.update(merchantId, {
frequency: 'monthly',
amount: 14.99,
});// List connected institutions
const institutions = await client.institutions.list();// Get credit score history
const scores = await client.creditHistory.get();// Get subscription details
const subscription = await client.subscription.get();Every method that returns processed data also accepts a { raw: true } option to get the unprocessed GraphQL response:
const raw = await client.accounts.list({ raw: true });
const raw = await client.institutions.list({ raw: true });
const raw = await client.creditHistory.get({ raw: true });import {
MonarchError,
LoginFailedError,
RequireMFAError,
RequestFailedError,
} from 'monarch-api';
try {
await client.login({ email, password });
} catch (err) {
if (err instanceof LoginFailedError) {
// Invalid credentials
} else if (err instanceof RequireMFAError) {
// MFA required
} else if (err instanceof RequestFailedError) {
// API request failed
} else if (err instanceof MonarchError) {
// Other SDK error
}
}This package is written in TypeScript and ships with full type definitions. All request options and response shapes are typed.
import type {
Account,
Transaction,
TransactionCategory,
Budget,
CashflowSummary,
RecurringTransaction,
} from 'monarch-api';This repo also includes an interactive terminal UI for exploring the SDK against a live Monarch account. It's useful for development and serves as a reference example of a client application built with monarch-api.
npm run dev:tuiThe TUI handles authentication automatically — it will prompt for credentials on first run and cache the session token to ~/.monarch-session.json for subsequent runs. You can also pre-fill credentials via environment variables:
MONARCH_EMAIL=you@example.com \
MONARCH_PASSWORD=your-password \
MONARCH_MFA_SECRET=YOUR_TOTP_SECRET \
npm run dev:tuiThe TUI is organized into modules, selectable from the left sidebar:
| Module | Description |
|---|---|
| Welcome | Verifies your session token, shows masked token and account count |
| Accounts | Lists all connected accounts with type and balance, plus total net worth |
| Transactions | Displays the last 30 days of transactions with date, merchant, category, and amount |
| Key | Action |
|---|---|
↑ / ↓ or j / k |
Navigate the module list |
Enter |
Launch the selected module |
Tab |
Toggle focus between sidebar and output pane |
↑ / ↓ |
Scroll output one line |
PageUp / PageDown |
Scroll output one page |
t / b |
Jump to top / bottom of output |
f |
Toggle follow mode (auto-scroll as output arrives) |
q |
Quit |
Modules are auto-discovered from tui/modules/*.ts. Each module exports a name string and a run(context) function:
import type { Module } from '../lib/types.js';
export const name = 'My Module';
export const run: Module = async ({ client, display, signal }) => {
display.header('My Module');
const accounts = await client.accounts.list();
display.table(accounts, ['name', 'currentBalance']);
};Distributed under the MIT License, see LICENSE.md for details.