API reference

Ledger API

Personal finance: accounts, transactions, categories, rules, imports, reports and notifications.

Personal finance ledger. Money is always integer cents (amount_cents, negative means money out). Dates are YYYY-MM-DD, months YYYY-MM, timestamps ISO 8601 UTC. Lists are JSON arrays. Auth: every route requires the X-Ledger-Token header. A missing, wrong or revoked token, or a method its scope does not allow, gets 404 not_found, the same as an unknown route. Paging: /v1/transactions, /v1/accounts/{id}/balances, /v1/holdings/history and /v1/notifications return X-Next-Cursor when more rows follow. Send it back as cursor=. Other lists return one page. Transactions also accept offset (0 to 1,000,000), but not together with cursor. Rate limits: 120 requests a minute per client address, counting every request. After 30 failed authentications in 15 minutes, the address gets 429 until the window ends. A 429 carries Retry-After in seconds. Idempotency: POST /v1/transactions and POST /v1/imports accept an Idempotency-Key header (1 to 200 characters). A retry with the same key gets the stored 2xx response, with Idempotent-Replayed: true. Versions: the path version is /v1. info.version is the revision of this document. Adding a field, endpoint or optional parameter does not change the path version.

Auth. Send the token in the X-Ledger-Token header. Some routes need none; each operation says so.

Health

MethodEndpointWhat it does
GET/v1/healthLiveness check

Accounts

MethodEndpointWhat it does
GET/v1/accountsEvery linked account with its most recent balance
GET/v1/accounts/{id}/portfolioAn account's holdings and cash, as of its latest balance date
GET/v1/accounts/{id}/balancesBalance snapshots for one account, newest first
DELETE/v1/accounts/{id}Delete an account. Refused while it holds transactions, balances, holdings or imports.
PATCH/v1/accounts/{id}Set an account's type, institution or nickname. The bank's own name is never changed.

Account types

MethodEndpointWhat it does
PATCH/v1/account-types/{name}Rename an account type everywhere it is used
DELETE/v1/account-types/{name}Delete an account type. If accounts use it, replace_with must name the type they move to.
GET/v1/account-typesThe account type list, with how many accounts use each type
POST/v1/account-typesAdd an account type. Adding an existing one is safe.

Transfers

MethodEndpointWhat it does
GET/v1/transfersTransactions matched as transfers between two of your accounts
POST/v1/transfersLink two transactions as a transfer by hand
DELETE/v1/transfers/{id}Remove a transfer link. The transactions go back to being ordinary spending or income.
GET/v1/transfers/reviewPossible transfers that could not be matched automatically

Rules

MethodEndpointWhat it does
GET/v1/rulesCategorization rules in the order they are tried
POST/v1/rulesAdd a rule. A pattern that already has a rule is reported, not changed.
POST/v1/rules/previewWhat a rule would match, without creating it
PATCH/v1/rules/{id}Change a rule's pattern, category or priority. Transactions are re-categorized immediately.
DELETE/v1/rules/{id}Remove a rule. Transactions it had categorized become uncategorized, then the remaining rules decide again.
POST/v1/rules/applyRe-run every rule. Manual categories are never changed.
GET/v1/rules/matchWhich rule would apply to a description, to test a rule before relying on it

Tokens

MethodEndpointWhat it does
GET/v1/tokensNamed API tokens, admin token only. The tokens themselves are never shown here.
POST/v1/tokensCreate a named token, admin token only. The plain token is returned once; only its hash is kept.
DELETE/v1/tokens/{id}Revoke a named token, admin token only. It stops working at once and cannot be restored; create a new one instead.

Sources

MethodEndpointWhat it does
GET/v1/sources/{id}/settingsA source's settings, admin token only. A source with no settings yet is off.
PUT/v1/sources/{id}/settingsTurn a source on or off, and set what it needs. Admin token only. Omitted fields keep their current values; config replaces the whole config. A source cannot be enabled until its config has profile_url. Only https URLs are accepted.

Openapi.yaml

MethodEndpointWhat it does
GET/v1/openapi.yamlThis contract, as YAML

Dashboard

MethodEndpointWhat it does
GET/v1/dashboard/sessionWhether the caller has a dashboard session. Public; carries no data.
GET/v1/dashboard/github/startStarts the owner's GitHub sign-in. Redirects to GitHub with a state and a PKCE challenge, and sets a five-minute cookie that holds the state and verifier.
GET/v1/dashboard/github/callbackGitHub's redirect back. Accepts only the owner's GitHub user id. On success it sets the session cookie and redirects to the dashboard. Any failure redirects to the dashboard with signin=failed and counts against the address.
POST/v1/dashboard/logoutEnds the dashboard session and clears its cookie. Public; a request without a session is still a success.

Status

MethodEndpointWhat it does
GET/v1/statusIs the data current? Counts, the uncategorized total, and the latest run of each source.

Net worth

MethodEndpointWhat it does
GET/v1/net-worthNet worth now, from each account's latest balance
GET/v1/net-worth/seriesNet worth at the end of each month

Summary

MethodEndpointWhat it does
GET/v1/summary/categoriesSpending and income by category over a date range. Transfers are excluded.
GET/v1/summary/cashflowIncome, spending, net and savings rate per month. Transfers are excluded.
GET/v1/summary/monthlySpending and income per account per month

Imports

MethodEndpointWhat it does
POST/v1/importsImport a Ledger CSV v1 file for one account. Format in docs/import-format.md.
GET/v1/importsPast imports, newest first
GET/v1/imports/template.csvA blank Ledger CSV v1 template with two example rows
DELETE/v1/imports/{id}Undo an import: remove every transaction it added. Bank and other imported rows are untouched.

Notification rules

MethodEndpointWhat it does
GET/v1/notification-rulesNotification rules
POST/v1/notification-rulesAdd a rule. Rules only see transactions and data that arrive after they are created.
PATCH/v1/notification-rules/{id}Change a rule's name, threshold, category or enabled flag. The event cannot change.
DELETE/v1/notification-rules/{id}Delete a rule and its inbox items

Notifications

MethodEndpointWhat it does
GET/v1/notificationsThe inbox, newest first
POST/v1/notifications/read-allMark every unread item as read
POST/v1/notifications/{id}/readMark one item as read. Marking it again changes nothing.

Merchants

MethodEndpointWhat it does
GET/v1/merchantsMerchants as they are shown, with alias names applied, most spending first. Transfers are excluded.

Merchant aliases

MethodEndpointWhat it does
GET/v1/merchant-aliasesThe alias list. The longest matching pattern wins.
POST/v1/merchant-aliasesName a merchant. Bank text containing the pattern is shown under the name. Bank text is never changed.
PATCH/v1/merchant-aliases/{id}Change an alias's pattern or name
DELETE/v1/merchant-aliases/{id}Remove an alias. Transactions show their normalized bank text again.

Categories

MethodEndpointWhat it does
PATCH/v1/categories/{name}Rename a category everywhere it is used (transactions and rules). Built-in categories cannot be renamed.
DELETE/v1/categories/{name}Delete a category. If it is in use, replace_with must name a category to move its transactions and rules to.
GET/v1/categoriesThe category list, with how many transactions use each one
POST/v1/categoriesAdd a category to the list. Adding an existing one is safe.

Transactions

MethodEndpointWhat it does
POST/v1/transactions/bulk-categorizeSet the category of up to 500 transactions in one request
POST/v1/transactions/{id}/splitSplit a transaction into parts that add up to its amount
DELETE/v1/transactions/{id}/splitUndo a split. Deletes its parts and counts the parent again.
GET/v1/transactions/export.csvThe transactions matching the same filters as the list, as CSV
DELETE/v1/transactions/{id}Delete a manual or Apple Pay transaction. Bank transactions cannot be deleted.
PATCH/v1/transactions/{id}Edit a transaction. Manual and Apple Pay entries can change every field; bank transactions can change only category and note. Sending any other field to a bank transaction returns 400.
GET/v1/transactionsTransactions, newest first
POST/v1/transactionsRecord a manual or Apple Pay transaction. Retrying with the same external_id, or the same Idempotency-Key, is safe.

Holdings

MethodEndpointWhat it does
GET/v1/holdings/historyHow each holding's value changed over time, newest first
GET/v1/holdingsInvestment positions with their latest market value

Budgets

MethodEndpointWhat it does
GET/v1/budgetsThe budgets in effect for a month, with what was spent in each category. Spending is outgoing amounts, with transfers and split parents left out.
GET/v1/budgets/suggestionsA suggested budget per category, the average monthly spend over the last 3 or 4 complete months, rounded up to the next dollar. Read only; nothing is saved.
PUT/v1/budgets/{category}Set a category's budget from a month on. Setting the same month again replaces that month's amount. Months before this one cannot be changed.
DELETE/v1/budgets/{category}End a category's budget from a month on. The month and the months before it keep their amounts; the budget is not listed from that month.

Schemas

Account

FieldTypeRequiredDescription
idstringrequiredSimpleFIN account id
conn_idstring or nulloptional
org_namestring or nulloptionalThe institution name as the bank reports it
institutionstring or nulloptionalYour name for the bank. Falls back to org_name.
namestringrequiredThe account name as the bank reports it
nicknamestring or nulloptionalYour name for the account
display_namestringoptionalnickname if set
created_atstring or null (date-time)optionalWhen this account was first seen
updated_atstring or null (date-time)optionalWhen its latest balance was recorded
currencystring or nulloptional
account_typestring or nulloptionalFrom the account type list. null until set.
balance_centsinteger or nulloptional
available_centsinteger or nulloptional
balance_datestring or null (date-time)optional

Balance

FieldTypeRequiredDescription
balance_datestring (date-time)requiredWhen the bank reported it
balance_centsintegerrequired
available_centsinteger or nulloptional
captured_atstring (date-time)optional

Transaction

FieldTypeRequiredDescription
idintegerrequired
sourcestring: one of manual, apple_pay, simplefinrequired
external_idstringrequired
account_idstring or nulloptional
postedstring (date)requiredBank posting date
transacted_atstring or null (date-time)optionalWhen the purchase happened
amount_centsintegerrequired
merchantstringrequiredThe bank's description
payeestring or nulloptional
memostring or nulloptional
categorystringoptionalNever null. Uncategorized until a rule or you set it.
category_sourcestring: one of manual, rule, transfer, defaultoptionalmanual: you set it; rule: a rule set it; transfer: it is a matched transfer; default: nothing has set it yet
rule_idinteger or nulloptionalThe rule that set the category
notestring or nulloptional

Portfolio

FieldTypeRequiredDescription
account_idstringrequired
namestringoptional
org_namestring or nulloptional
currencystring or nulloptional
as_ofstring or null (date-time)optionalThe account's latest balance date
balance_centsinteger or nulloptional
holdings_value_centsinteger or nulloptionalSum of holdings at as_of; null if any holding has no value
cash_balance_centsinteger or nulloptionalbalance_cents minus holdings_value_cents; derived
cash_is_derivedboolean: one of Truerequired
holdingsobject[]required

AccountType

FieldTypeRequiredDescription
namestringrequired
accountsintegerrequiredNumber of accounts using this type

Transfer

FieldTypeRequiredDescription
idintegerrequired
amount_centsintegerrequiredPositive amount moved
methodstring: one of auto, manualrequired
created_atstring (date-time)optional
out_tx_idintegerrequiredThe money-out side
from_account_idstringrequired
out_postedstring (date)optional
in_tx_idintegerrequiredThe money-in side
to_account_idstringrequired
in_postedstring (date)optional

Category

FieldTypeRequiredDescription
namestringrequired
transactionsintegerrequiredNumber of transactions in this category

ApiToken

FieldTypeRequiredDescription
idintegerrequired
namestringrequired
scopestring: one of read, writerequired
created_atstring (date-time)required
last_used_atstring or null (date-time)optional
revoked_atstring or null (date-time)optional

SourceSettings

FieldTypeRequiredDescription
sourcestring: one of collectrrequired
enabledbooleanrequired
configobjectrequired
updated_atstring or null (date-time)required

Rule

FieldTypeRequiredDescription
idintegerrequired
patternstringrequired
categorystringrequired
priorityintegerrequired
created_atstring (date-time)optional
transactionsintegeroptionalTransactions currently set by this rule

RuleCreated

FieldTypeRequiredDescription
idintegerrequired
patternstringrequired
categorystringrequired
createdbooleanrequired

CategoryCreated

FieldTypeRequiredDescription
namestringrequired
createdbooleanrequiredfalse when the category already existed

Import

FieldTypeRequiredDescription
idintegerrequired
account_idstringrequired
filenamestring or nulloptional
created_atstring (date-time)required
rows_totalintegerrequired
rows_newintegerrequired
rows_duplicateintegerrequired

ImportResult

FieldTypeRequiredDescription
dry_runbooleanrequired
import_idintegeroptionalPresent when the import was written
rows_totalintegerrequired
rows_newintegerrequired
rows_duplicateintegerrequired
uncategorizedintegeroptionalNew rows still Uncategorized after the rules ran
categorized_by_youintegeroptionalDry run only: rows with a category in the file
categorized_by_ruleintegeroptionalDry run only: rows the rules would categorize

NotificationRule

FieldTypeRequiredDescription
idintegerrequired
namestringrequired
eventstring: one of income, large_spend, spend_digest, transaction, balance_below, sync_failed, uncategorized_overrequired
thresholdinteger or nulloptional
categorystring or nulloptional
topicstring or nulloptional
enabledbooleanrequired
created_atstring (date-time)optional

Notification

FieldTypeRequiredDescription
idintegerrequired
rule_idintegerrequired
titlestringrequired
detailstring or nulloptional
transaction_idinteger or nulloptional
created_atstring (date-time)required
read_atstring or null (date-time)optional

MerchantAlias

FieldTypeRequiredDescription
idintegerrequired
patternstringrequired
namestringrequired
created_atstring (date-time)optional
createdbooleanoptionalPresent on POST only

Holding

FieldTypeRequiredDescription
idstringrequired
account_idstringrequired
symbolstring or nulloptional
descriptionstring or nulloptional
currencystring or nulloptional
sharesnumber or nulloptional
purchase_pricestring or nulloptionalPer-share price as the bank sent it
cost_basis_centsinteger or nulloptional
created_atstring or null (date-time)optional
as_ofstring or null (date-time)optionalDate of the latest snapshot
market_value_centsinteger or nulloptional

MonthlySummary

FieldTypeRequiredDescription
account_idstring or nullrequired
monthstringrequired
spent_centsintegerrequired
received_centsintegerrequired
countintegerrequired

NewTransaction

FieldTypeRequiredDescription
sourcestring: one of manual, apple_payrequired
external_idstringoptionalClient-generated unique id, e.g. a UUID per entry
postedstring (date)required
amount_centsintegerrequiredNon-zero, magnitude up to 10000000
merchantstringrequired
categorystringoptionalMust be on the category list
account_idstringoptionalMust match an existing account
notestringoptional

Created

FieldTypeRequiredDescription
createdbooleanrequiredfalse when the transaction already existed

Error

FieldTypeRequiredDescription
errorstringrequiredHuman-readable message
codestringrequiredStable machine-readable code, such as invalid_request, not_found, conflict, rate_limited, invalid_split, unsupported_media_type, payload_too_large, internal_error