I just finished my Q1 2026 estimated payment and I’m once again staring at the same problem that haunts every freelancer who tracks their finances in Beancount: my income is wildly irregular, but the IRS expects predictable quarterly payments.
Here’s my actual 2025 income pattern (rounded for privacy):
| Month | Freelance Income |
|---|---|
| January | $2,400 |
| February | $14,800 |
| March | $1,100 |
| April | $21,500 |
| May | $3,200 |
| June | $8,900 |
| July | $900 |
| August | $16,300 |
| September | $4,100 |
| October | $19,700 |
| November | $2,800 |
| December | $11,400 |
That’s $107,100 total—but look at the variance. January vs. April is nearly a 10x difference. Standard advice says “pay 25% of last year’s tax each quarter,” but that creates two painful scenarios:
Scenario A: Overpayment in lean quarters. If I’m paying $6,800/quarter based on last year’s liability, but Q1 actual income was only $18,300… I’m locking up cash I might need for rent during a dry spell. For someone pursuing FIRE, that’s money that could be invested for 9+ months earning returns.
Scenario B: Underpayment when income spikes. If I use the “pay based on actual income” approach but have a massive Q4 ($31,100 in Oct-Dec 2025), I’d owe a huge payment that I might not have liquid if I’d already invested aggressively.
What I’ve Built in Beancount So Far
I wrote a Python script that queries my current-year Beancount ledger after each income entry and calculates:
- Year-to-date self-employment income (BQL query filtering
Income:Freelanceaccounts) - Estimated annual tax liability using current brackets + 15.3% SE tax
- Quarterly payments already made (tracking via
Expenses:Taxes:Federal:Estimated) - Remaining liability and what I should pay next quarter
The basic flow:
from beancount import loader
from beancount.query import query
entries, errors, options = loader.load_file("main.beancount")
results = query.run_query(entries, options,
"SELECT sum(position) WHERE account ~ 'Income:Freelance' AND year = 2026")
ytd_income = abs(results[1][0][0].get_only_position().units.number)
# Calculate SE tax + income tax using bracket logic
se_tax = float(ytd_income) * 0.9235 * 0.153
# ... bracket calculations ...
It works, but it’s crude. The biggest gap: it doesn’t account for the annualized income installment method (Form 2210 Schedule AI), which could save me thousands by matching payments to when income actually arrives instead of spreading evenly.
The Real Question
For those of you who freelance or have clients who do:
-
Do you use the prior-year safe harbor (100%/110% of last year) and just accept overpayment as the price of simplicity? The 2026 safe harbor requires paying 110% of your 2025 tax if your AGI exceeded $150K.
-
Has anyone implemented the annualized income installment method in Beancount? This would calculate required payments based on income received in each period (Jan-Mar, Jan-May, Jan-Aug, Jan-Dec) rather than assuming equal distribution. The math is more complex but could significantly reduce early-year payments when income is back-loaded.
-
Multi-state freelancers: If you earned income in 3 states in 2026, are you tracking state-level estimated payments separately in Beancount? My current account structure is:
Expenses:Taxes:Federal:Estimated
Expenses:Taxes:State:CA:Estimated
Expenses:Taxes:State:NY:Estimated
But I’m not sure this is the right approach for state-level annualization.
- The behavioral question: Does seeing your real-time tax liability in Beancount actually change your payment behavior? Or do you still procrastinate and hope for the best in April?
The underpayment penalty rate is approximately 7% annualized right now (federal short-term rate + 3 percentage points). That’s not trivial—on a $5,000 underpayment for one quarter, you’re looking at ~$87 in penalties. Across a year of misestimation, it adds up.
I’d love to hear how others handle this, especially if you’ve built more sophisticated forecasting into your Beancount workflow.