Budgets
Read your company's budgets over the LedgerBee public API (base URL
https://api.ledgerbee.com/api/v1).
Budgets are created and edited in the LedgerBee app; the API exposes them
read-only. Authenticate with the x-api-key header. There is one scope:
budgets-read— every budget read.
The Budgets module is licensed: calls return 403 LICENSE.REQUIRED unless your
company holds the Budgets license.
A budget plans one accounting year as a grid of amounts per ledger account and
month. Every amount crosses the wire as a decimal string ("160000.00") in
your company's base currency, which each response states as currency.
Operations
| Operation | Endpoint | Scope |
|---|---|---|
| List budgets | GET /v1/budgets | budgets-read |
| Runway headline | GET /v1/budgets/runway | budgets-read |
| Get a budget | GET /v1/budgets/{budgetId} | budgets-read |
| List scenarios | GET /v1/budgets/{budgetId}/scenarios | budgets-read |
| Scenario grid | GET /v1/budgets/{budgetId}/scenarios/{scenarioId}/grid | budgets-read |
| Budget vs actuals | GET /v1/budgets/{budgetId}/comparison | budgets-read |
| Variance vs frozen baseline | GET /v1/budgets/{budgetId}/variance | budgets-read |
| List frozen baselines | GET /v1/budgets/{budgetId}/frozen-baselines | budgets-read |
| Get a frozen baseline | GET /v1/budgets/{budgetId}/frozen-baselines/{baselineId} | budgets-read |
| Re-baseline proposals | GET /v1/budgets/{budgetId}/rebaseline-proposals | budgets-read |
The list is paginated (page, limit), filterable by status,
accountingYearId, and search (prefix match on name), and sortable by
name, createdAt, or updatedAt. Endpoint schemas are in the
API Reference.
Code
The budget grid
GET /v1/budgets/{budgetId} returns the full grid: lines (one cell per
account and period), the accounting year's periods, driverRows
(non-monetary planning quantities such as headcount, with numeric values), and
per-budget display metadata (rowOverrides, rowNumbers).
Cells in periods whose status is CLOSED carry the booked actuals from the
ledger rather than the planned amount, marked source: "ACTUALS" — the same
rolling-forecast view the app shows. A cell filled from the subscription
forecast is marked source: "SUBSCRIPTION"; a cell with source: null is
user-entered.
Cells can carry a formula (for example =baseline*1.05 or =#3*2); the
resolved value is always cached in amount, so you never need to evaluate
formulas. #N references resolve through the rowNumbers array. Pass
?includeBaseline=true to add each cell's prior-year realised amount as
baseline.
Code
Scenarios
A scenario is an assumption overlay (driver overrides and line toggles) on top
of the budget's base grid. The scenario flagged isBaseline: true is the base
grid with no overlay; it is created the first time scenarios are used in the
app, so a budget where they have never been opened returns an empty list — the
base data is the budget detail itself.
GET /v1/budgets/{budgetId}/scenarios/{scenarioId}/grid returns the fully
resolved grid with the scenario's assumptions applied. When the response has
hasCycle: true, the overlay could not resolve because of a formula cycle;
the offending cells carry error: "BUDGET_FORMULA_CYCLE" and amounts must not
be trusted.
Comparison and variance
Two reports compare the budget against booked actuals, in opposite directions:
| Report | Compares | Variance |
|---|---|---|
GET .../comparison | Live plan vs actuals, per account and period | budget − actual |
GET .../variance | Actuals vs a frozen baseline, closed periods only | actual − frozenBaseline |
The comparison accepts viewMode=FISCAL_YEAR (the budget's accounting year,
the default) or viewMode=ROLLING_12_MONTHS with an optional startMonth
(YYYY-MM). Each entry's isOverBudget marks the unfavourable direction for
that account type: overspend on expense accounts, shortfall on revenue
accounts.
The variance report compares against a frozen baseline — an immutable snapshot
of the resolved grid taken in the app. It defaults to the most recently frozen
baseline; pass ?frozenBaselineId= to pick one from
GET /v1/budgets/{budgetId}/frozen-baselines. A budget with no frozen
baseline returns an empty data array. Each row's sentiment is
unfavourable when spending exceeds the frozen plan (expense accounts) or
revenue misses it (revenue accounts).
GET /v1/budgets/{budgetId}/rebaseline-proposals lists the re-baseline
suggestions raised when accounting periods close, with their status
(PROPOSED, CONFIRMED, or REJECTED). Proposals are confirmed or rejected
in the app.
Runway
GET /v1/budgets/runway returns the company-wide cash headline: cash on hand
(the sum of bank-account-linked ledger balances), the trailing 3-month burn,
and monthsRemaining. Cash and burn are properties of the whole company, so
the endpoint is not scoped under a budget id. Pass ?asOf=YYYY-MM-DD to
compute as of a past date. When no bank account is linked to a ledger account,
hasCashSource is false and status is UNKNOWN.
Errors
| Condition | Error code | Fix |
|---|---|---|
| Company does not hold the Budgets license | 403 LICENSE.REQUIRED | Activate the Budgets module in LedgerBee. |
| Key lacks the scope | 403 INSUFFICIENT_PERMISSIONS | Mint a key with budgets-read. |
Unknown or foreign budgetId | 404 BUDGET_NOT_FOUND | Use an id from GET /v1/budgets. |
Unknown scenarioId | 404 BUDGET_SCENARIO_NOT_FOUND | Use an id from the scenarios list. |
Unknown baselineId | 404 BUDGET_FROZEN_BASELINE_NOT_FOUND | Use an id from the frozen-baselines list. |
| Invalid filter or query value | A 400 validation error | See the API Reference for accepted values. |