Simply Family Budget Public API (1.0.0)

Download OpenAPI specification:

A read-only API for user-authorized family budgeting dashboards, including Home Assistant.

Authentication and rotation

Create a named public API token in Settings → Security after reauthentication. The full token is shown once; Simply Family Budget stores only its hash and metadata. Send it only in the HTTPS Authorization: Bearer <token> header. Tokens are bound to the issuing user and family, expire after 365 days, and stop working immediately after revocation, family-membership removal, or a credential reset. Never place a token in a URL.

Data conventions and privacy

Money is returned as integer cents, field names are snake_case, timestamps are UTC ISO-8601, and date-only filters use the family's timezone. Transaction notes are intentionally returned and may contain sensitive information; secure connected systems and avoid showing notes on publicly visible dashboards. Third-party systems process copied data under their own privacy and security practices.

Transaction aggregation

Splits: /transactions and overview.recent_transactions return only parent transactions, once each, with the full amount_cents. Split allocations are not additional transactions and are not exposed. Sum each parent's amount once for income/spending totals. The returned category and category_id filter refer only to the original parent category; they do not describe or filter split allocations. Grouping full parent amounts by category cannot reproduce split-aware category spending. Use /budgets for split-aware spending on categories with budgets; v1 does not expose a complete category allocation ledger.

Deleted and inactive records: Soft-deleted and permanently deleted transactions are excluded from the list, recent transactions, and month totals. There is no public include-deleted option or deletion feed. Transactions have no separate cancelled, voided, or inactive status; a note or metadata label does not exclude a record. Archiving a wallet or category, excluding a wallet from balance totals, or pausing/deleting a recurrence does not remove existing transaction history from these reads. Clients retaining a local copy must refresh their reporting range to detect removals.

Refunds and signs: Supported transaction writes require positive integer amount_cents; negative amounts and zero are not supported. Direction is determined by type: expense is outgoing, income is incoming, and transfer moves funds between wallets without contributing to income/spending. There is no dedicated refund, chargeback, or reversal-link mechanism. A refund entered as positive income increases month.income_cents and month.net_cents, but does not reduce month.spending_cents. Reducing or deleting the original expense changes spending in the original expense's month. Do not infer a refund from the note or negate an amount based on its text.

Nullable categories: Current expense/income writes require a matching family category; transfers require no category. The read contract nevertheless allows category: null for any type when an existing record has no resolvable category in the token's family. Null does not identify a transfer: use type, and retain uncategorized expense/income amounts in totals. Archived categories still resolve.

Reconciling the overview

month is month-to-date: from midnight on the first day of month.period in timezone, through generated_at inclusive. spending_cents sums the full amounts of non-deleted expense parents using the same family and record inclusion rules as /transactions; income_cents does the same for income, and net_cents = income_cents - spending_cents. Wallet/category archival, excluded wallets, and null categories do not exclude amounts. Splits do not change the overall total.

To reconcile, request /transactions?type=expense&from=YYYY-MM-01&to=YYYY-MM-DD, using month.period for the first date and the family-local date of generated_at for the last. Follow every next_cursor with the same filters, keep only rows whose occurred_at <= generated_at (compare instants), then sum amount_cents. Repeat with type=income for income. Date-only to includes the entire local day; it is not an instant cutoff. A whole-month or whole-day sum can include future-dated stored transactions that the overview excludes. Unmaterialized recurrence occurrences are not transactions.

The list and month totals read the database directly without an application cache and responses use Cache-Control: private, no-store; there is no cache convergence window. Requests, overview sections, and cursor pages do not share a database snapshot. Concurrent creates, edits, deletions, or recurrence materialization can change results between reads; generated_at is an occurrence-time cutoff, not a historical snapshot or change cursor. Refetch the overview and reporting range after writes settle.

Limits, errors, and pagination

Each token is limited to 30 requests/minute and 600/hour; source IPs are limited to 120/minute. A 429 response includes Retry-After and rate-limit headers. Invalid, expired, revoked, orphaned, and credential-stale tokens receive a generic 401; valid credentials of the wrong type or scope receive 403. Transaction pages use opaque next_cursor and previous_cursor values. Do not inspect or construct cursors.

curl

curl --fail --silent --show-error \
  --header "Authorization: Bearer $SFB_PUBLIC_API_TOKEN" \
  https://api.simply-family-budget.com/api/public/v1/overview

Home Assistant REST sensor

Store the complete Bearer ... value in secrets.yaml, for example sfb_api_authorization: "Bearer 1|...", then reference it with !secret as recommended by Home Assistant's RESTful Sensor documentation.

sensor:
  - platform: rest
    name: Simply Family Budget total balance
    resource: https://api.simply-family-budget.com/api/public/v1/overview
    headers:
      Authorization: !secret sfb_api_authorization
    value_template: "{{ (value_json.data.total_balance_cents / 100) | round(2) }}"
    unit_of_measurement: CAD
    scan_interval: 300

Rotate the token at least annually and revoke it immediately if it appears in logs, screenshots, source control, or an exposed dashboard.

Overview

Overview

Get the family dashboard overview

Month totals cover the family-local month start through generated_at inclusive. They count each non-deleted transaction parent once, including history in archived/excluded wallets. Database reads are uncached but do not form a shared snapshot with transaction pages. See Reconciling the overview.

Authorizations:
public_api_token

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "data": {
    },
  • "error": null
}

Wallets

Wallets

List active wallets and balances

Authorizations:
public_api_token

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "data": [
    ],
  • "error": null
}

Budgets

Budgets

List budgets for a family-local month

Authorizations:
public_api_token
query Parameters
period
string^\d{4}-(0[1-9]|1[0-2])$

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "data": {
    },
  • "error": null
}

Transactions

Transactions

List safe transaction records with opaque cursor pagination

Returns non-deleted parents only, with full amounts and original categories. Split allocations are not exposed. Archived/excluded wallets and archived/null categories do not exclude records. Future-dated stored transactions are included when they match the filters. Read every page and apply occurred_at <= overview.generated_at when reconciling month-to-date totals. Pages are live reads, not a snapshot or deletion feed.

Authorizations:
public_api_token
query Parameters
cursor
string
limit
integer [ 1 .. 100 ]
Default: 20
type
string
Enum: "expense" "income" "transfer"
wallet_id
string <uuid>
category_id
string <uuid>

Filter by original parent category only; does not match split allocation categories or allocate the returned amount.

from
string <date>

Inclusive family-local date.

to
string <date>

Inclusive family-local date.

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "data": {
    },
  • "error": null
}