beancount_ex can run Beancount Query Language
(BQL) queries against a ledger through the configured engine. The CLI
engine shells out to bean-query from the beanquery
package; the native engine and external engines implement the same Beancount.Engine.query/2
callback.
Install with: pip install beanquery (Beancount v3 does not bundle bean-query
in the beancount package alone).
Running a query
All three entry points mirror the check/* family:
ledger = [
Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"]),
Beancount.open(~D[2026-01-01], "Income:Salary", ["USD"]),
Beancount.transaction(~D[2026-01-31], "*", "Employer", "Salary", [
Beancount.posting("Assets:Bank", Decimal.new("5000"), "USD"),
Beancount.posting("Income:Salary", Decimal.new("-5000"), "USD")
])
]
{:ok, result} =
Beancount.query(ledger, "SELECT account, sum(position) GROUP BY account")
# raw text instead of directives
{:ok, result} = Beancount.query_text(bean_text, "SELECT account")
# a file on disk
{:ok, result} = Beancount.query_file("ledger.bean", "SELECT account")The result
A successful query returns a neutral, engine-independent
Beancount.Query.Result:
%Beancount.Query.Result{
columns: ["account", "balance"],
rows: [["Assets:Bank", "5000 USD"], ["Income:Salary", "-5000 USD"]],
raw: "account,balance\r\n...",
status: :ok
}Cells are kept as raw strings so the result stays backend-neutral. Convert to a
list of maps with Beancount.Query.Result.to_maps/1, or to an
Explorer.DataFrame via the optional bridge (see
Reporting).
Errors
A query that fails (bad BQL, etc.) returns {:error, %Beancount.Result{}} with
normalized error details - the same shape as a failed check. A missing
bean-query binary raises Beancount.Query.NotInstalledError, keeping
environment problems separate from query errors.
Configuration
config :beancount_ex, bean_query_path: "bean-query"