Introduction

Tax accounting is one of the most crucial aspects of personal and business bookkeeping, yet it can be intimidating for newcomers to double-entry accounting systems like Beancount. This guide will walk you through the fundamental concepts and practical techniques for accurately recording various types of taxes in your Beancount ledger.

The Beancount example in this post can be found in: https://github.com/flyaway1217/beancount_example/blob/main/single_examples/taxes.bean

Tax Categories

Before diving into specific examples, it’s important to understand how taxes fit into your daily life. Taxes generally fall into several categories:

  • Income Taxes: Federal, state, and local income taxes that you owe or have paid.
  • Payroll Taxes: Social Security, Medicare, unemployment insurance, and other employment-related taxes
  • Sales and Use Taxes: Taxes paid on purchases.
  • Capital gain Taxes: This is usually reflected in the annual tax return forms. There is no need to set up a specific Expenses account for capital gain taxes.
  • Property Taxes: Real estate and personal property taxes.

Setting Up Your Tax Account Structure

A well-organized account hierarchy is essential for tax tracking. Each tax category has its own branch in the Expenses hierarchy except the property taxes. I put the property taxes under the Expenses accounts of the corresponding real estate, which makes more senses to me.

The Expenses account hierarchy I recommend is as following:

2024-01-01 open Expenses:Taxes:Federal:IncomeTax:Withhold
2024-01-01 open Expenses:Taxes:Federal:IncomeTax:Payments
2024-01-01 open Expenses:Taxes:Federal:IncomeTax:Refund
; Social Security
2024-01-01 open Expenses:Taxes:Federal:SocialSecurityTax
; Medical Care
2024-01-01 open Expenses:Taxes:Federal:MedicareTax
; Sale Tax
2024-01-01 open Expenses:Taxes:SaleTax

Following the Computing Taxes with Beancount , I split the federal income taxes further into three subaccounts:

  • Withhold: This account records the amount withheld by the government throughout the year. Note that this does not reflect your total federal income tax liability.
  • Payments: This account captures any additional tax payments made when filing your tax return, typically in the second year, if you owed more than what was withheld.
  • Refund: This account records any tax refunds received, if applicable, when the total withheld and paid exceeds your actual tax liability.

The sum of the above three accounts is your total federal income tax expenses, which reflects in the Expenses:Taxes:FederalIncomeTax. This setup provides a clear breakdown of how your federal income tax is composed.

Recording Taxes Based on Paychecks

When you receive your paychecks, it usually states

  • The amount withheld for taxes
  • The total pre-tax deductions
  • The total post-tax deductions
  • Contributions to pre-tax accounts
  • Contributions to post-tax accounts
  • The net pay

Translating a paycheck into your Beancount ledger involves many considerations. For simplicity, this post focuses solely on the tax-related aspects of a paycheck. A comprehensive guide to mapping an entire paycheck into Beancount will be covered in a dedicated blog post.

Now, let us assume an example paycheck looks like:

1
2
3
4
5
Gross Pay:                $6,000.00
Federal Income Tax:       $1,200.00
Social Security Tax:        $372.00
Medicare Tax:                $87.00
Net Pay (Direct Deposit): $4,341.00

To convert this simplified paycheck into a Beancount transaction:

2025-07-15 * "Employer" "Salary"
    ; Gross income
    Income:Work:Salary                             -6,000 USD
    ; Tax
    Expenses:Taxes:Federal:IncomeTax:Withhold    1,200.00 USD
    Expenses:Taxes:Federal:SocialSecurityTax       372.00 USD
    Expenses:Taxes:Federal:MedicareTax              87.00 USD
    ; Direct deposit
    Assets:Cash:Checking:Chase                   4,341.00 USD

Handling Tax Returns

One challenge in recording tax expenses is that we can’t finalize them until we file our tax returns, which typically occurs in the following year. If we record additional tax payments or refunds in the year they’re received, it can misrepresent the previous year’s tax expenses when we later analyze them.

Let us use a specific example to demonstrate the issue and solutions.

Tax Return Examples

Suppose in 2024, your total income amounts to 100,000 dollars. Your employer withholds 10,000 dollars for federal income taxes. Upon filing your tax return in April 2025, your CPA determines a total tax liability of 13,000 dollars for 2024, leading you to pay an additional 3,000 dollars.

To log the salary and the withheld taxes for 2024, you can consolidate them into a single transaction dated December 1, 2024:

2024-12-01 * "Employer" "Salary"
    ; Gross income
    Income:Work:Salary                           -100,000.00 USD
    ; Tax
    Expenses:Taxes:Federal:IncomeTax:Withhold      10,000.00 USD
    ; Direct deposit
    Assets:Cash:Checking:Chase                     90,000.00 USD

This transaction reflects the gross income, the amount withheld for taxes, and the net salary deposited into your checking account.

Next, we need to record the extra tax payment. A naive way of doing it is:

2025-04-15 * "IRS" "Tax payments for 2024"
    Expenses:Taxes:Federal:IncomeTax:Payments    3,000.00 USD
    Assets:Cash:Checking:Chase                  -3,000.00 USD

However, this approach inaccurately attributes the expense to 2025, whereas it pertains to the 2024 tax year. This misclassification can lead to discrepancies when analyzing expenses for 2024.

Option 1: Dedicated Subaccounts for each Year

One method to accurately attribute the expense to 2024 is by creating dedicated subaccounts for that tax year:

2024-01-01 open Expenses:Taxes:FederalIncomeTax:2024:Withhold
2024-01-01 open Expenses:Taxes:FederalIncomeTax:2024:Payments
2024-01-01 open Expenses:Taxes:FederalIncomeTax:2024:Refund

Then, we can rewrite the extra tax payments as:

2025-04-15 * "IRS" "Tax payments for 2024"
    Expenses:Taxes:Federal:IncomeTax:2024:Payments    3,000.00 USD
    Assets:Cash:Checking:Chase                       -3,000.00 USD

In this way, the account Expenses:Taxes:FederalIncomeTax:2024 summarizes the total tax expenses for 2024.

Option 2: Use Two Separate Transactions

However, Option 1 is not the optimal solution. Although it correctly logs the total tax expenses for the 2024, it fails to correctly represent the total expenses for the 2024. If we want to query for the total expenses for 2024, we are still in short of the 3000 because that expenses is still logged in 2025.

The underlying issue here is that Beancount requires all postings in a transaction to share the same date, but in our case, the extra tax payment or refund occurs in the following year, while the expense pertains to the previous year. The postings in the same transaction happens in different dates.

So, following this thought, we can split the tax payment into two different transactions:

2024-12-31 * "IRS" "Tax payments for 2024"
    Expenses:Taxes:Federal:IncomeTax:2024:Payments                 3,000.00 USD
    Liabilities:Hold:Expenses:Taxes:Federal:IncomeTax:Payments    -3,000.00 USD


2025-04-15 * "IRS" "Tax payments for 2024"
    Liabilities:Hold:Expenses:Taxes:Federal:IncomeTax:Payments     3,000.00 USD
    Assets:Cash:Checking:Chase                                    -3,000.00 USD

The first transaction records the tax expense on 2024‑12‑31, using a Liabilities account to balance the entry. No cash actually changes hands yet—this transaction simply reflects the tax liability incurred in 2024. Then, when you file your taxes in 2025, you make the payment and zero out the liability by paying off the Liabilities account to record the real cash outflow.

This two-steps method ensures that:

  • The tax expense is correctly captured in 2024, and
  • The cash movement is reflected in 2025.

All that’s required is an additional Liabilities account to hold the obligation in between.

This same approach can be applied whenever an expense and its cash settlement occur in different fiscal periods.

Plugin to Automate

Although manually applying this accrual accounting technique works, it can become tedious to remember all the details. Fortunately, the effective_date plugin automates exactly this process.

What the plugin does:

  • Lets you assign different dates to individual postings within a single transaction.
  • Automatically splits the entry at load-time, using a virtual holding account to bridge the period between the expense and the payment dates.
  • Ensures that the expense is recorded in the correct fiscal year, while the cash outflow is logged when it actually occurs.

For our example, we can write the transaction as:

2025-04-15 * "IRS" "Tax payments for 2024"
    Expenses:Taxes:Federal:IncomeTax:Payments        3,000.00 USD
        effective_date: 2024-12-31
    Assets:Cash:Checking:Chase                      -3,000.00 USD

When processed, the plugin creates two logical postings:

  • Expense logged as of 2024‑12‑31 in the proper year.
  • Cash reduction recorded when the payment happens in 2025‑04‑15 using a holding account under the hood.

This keeps your records accurate and consistent across fiscal periods—without requiring manual juggling of multiple transactions or extra liability accounts.

The details of how the Beancount plugin works are beyond the scope of this post. We’ll explain how to configure and use the plugin in a separate blog post focused on the Beancount plugin mechanism.

Recording Sales Tax on Purchases

When you’re buying something that’s subject to sales tax, it’s simple to include the tax in the total purchase price:

2025-07-19 * "AMAZON" "buy grocery"
    Expenses:Daily:Grocery          12.32 USD
    Expenses:Taxes:SaleTax           1.28 USD
    Assets:Cash:Checking:Chase     -13.60 USD

Conclusion

Recording taxes accurately in Beancount requires understanding both tax concepts and double-entry bookkeeping principles. By establishing a clear account structure, maintaining consistent recording practices, and leveraging Beancount’s reporting capabilities, you can create a comprehensive tax tracking system that will serve you well during tax season and beyond.

Remember that while Beancount is an excellent tool for organizing your financial data, it’s always wise to consult with a qualified tax professional for complex tax situations or when in doubt about proper tax treatment of specific transactions.

The investment in time to properly set up and maintain your tax accounts in Beancount will pay dividends in reduced stress, better financial visibility, and more efficient tax preparation processes.