Download OpenAPI specification:
A read-only API for user-authorized family budgeting dashboards, including Home Assistant.
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.
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.
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.
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.
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 --fail --silent --show-error \
--header "Authorization: Bearer $SFB_PUBLIC_API_TOKEN" \
https://api.simply-family-budget.com/api/public/v1/overview
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.
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.
{- "success": true,
- "data": {
- "currency": "CAD",
- "timezone": "America/Toronto",
- "generated_at": "2019-08-24T14:15:22Z",
- "total_balance_cents": 0,
- "active_wallet_count": 0,
- "month": {
- "period": "2026-08",
- "income_cents": 0,
- "spending_cents": 0,
- "net_cents": 0
}, - "budget": {
- "limit_cents": 0,
- "spend_cents": 0,
- "remaining_cents": 0,
- "usage_percentage": 0.1
}, - "projected_end_of_month_balance_cents": 0,
- "recent_transactions": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "expense",
- "currency": "CAD",
- "amount_cents": 1299,
- "occurred_at": "2019-08-24T14:15:22Z",
- "note": "Weekly groceries",
- "wallet": {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string"
}, - "to_wallet": {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string"
}, - "category": {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string"
}
}
]
}, - "error": null
}{- "success": true,
- "data": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string",
- "type": "chequing",
- "current_balance_cents": 0,
- "total_contribution_cents": 0,
- "excluded_from_total": true,
- "credit_limit_cents": 0,
- "available_credit_cents": 0,
- "utilization_percentage": 0
}
], - "error": null
}| period | string^\d{4}-(0[1-9]|1[0-2])$ |
{- "success": true,
- "data": {
- "period": "string",
- "budgets": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "category_id": "8de4c9fd-61a4-4c0b-bf88-0ed3a0fe3fa2",
- "category_name": "string",
- "period": "2026-08",
- "limit_cents": 0,
- "spend_cents": 0,
- "remaining_cents": 0,
- "usage_percentage": 0.1,
- "status": "on_track"
}
]
}, - "error": null
}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.
| 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. |
{- "success": true,
- "data": {
- "transactions": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "expense",
- "currency": "CAD",
- "amount_cents": 1299,
- "occurred_at": "2019-08-24T14:15:22Z",
- "note": "Weekly groceries",
- "wallet": {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string"
}, - "to_wallet": {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string"
}, - "category": {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string"
}
}
], - "pagination": {
- "next_cursor": "string",
- "previous_cursor": "string",
- "per_page": 1,
- "has_more": true
}
}, - "error": null
}