The regulatory landscape for environmental reporting has shifted dramatically. California’s SB 253 and SB 261 now require large companies doing business in the state to report Scope 1 and 2 emissions by 2026, with Scope 3 following in 2027. The EU’s Corporate Sustainability Reporting Directive (CSRD) kicks in with Scope 3 reporting starting in 2027, based on 2026 data. ESG metrics are no longer optional—they’re becoming a compliance requirement, and investors are demanding transparency.
The problem? Commercial carbon accounting software runs anywhere from $3,000 to $250,000 per year, depending on organizational complexity. For small firms, B Corps, and sustainability-focused businesses, that’s a significant barrier to entry.
But here’s what occurred to me: we’re already tracking financial flows in Beancount. Why not track carbon flows the same way?
The Integration Advantage
The beauty of Beancount’s design is that it’s not really an “accounting tool”—it’s a double-entry ledger system that happens to work brilliantly for money. But the same principles apply to any quantifiable resource, including carbon dioxide equivalent (CO2e) emissions.
What if we could generate sustainability reports right alongside financial reports, from a single source of truth, with the same audit trail and version control we rely on for financial compliance?
A Practical Approach
Here’s how I’m thinking about structuring this:
Custom Commodities for Emissions
Just like we track USD, EUR, and stock shares, we can define CO2e as a commodity:
2020-01-01 commodity CO2e
name: "Carbon Dioxide Equivalent"
precision: 2
Account Structure for Scope Separation
Following the GHG Protocol’s Scope 1/2/3 framework:
2020-01-01 open Assets:Carbon:Scope1:DirectEmissions CO2e
2020-01-01 open Assets:Carbon:Scope1:CompanyVehicles CO2e
2020-01-01 open Assets:Carbon:Scope1:FacilityFuel CO2e
2020-01-01 open Assets:Carbon:Scope2:PurchasedElectricity CO2e
2020-01-01 open Assets:Carbon:Scope2:PurchasedHeating CO2e
2020-01-01 open Assets:Carbon:Scope3:BusinessTravel CO2e
2020-01-01 open Assets:Carbon:Scope3:SupplierEmissions CO2e
2020-01-01 open Assets:Carbon:Scope3:WasteDisposal CO2e
2020-01-01 open Equity:Carbon:Opening CO2e
Transactions with Emission Metadata
The key is capturing emissions alongside the financial transaction that caused them:
2026-03-15 * "Office electricity bill - March 2026"
Expenses:Utilities:Electricity 350.00 USD
Assets:Carbon:Scope2:PurchasedElectricity 450.0 CO2e
kwh-consumed: "1250"
emission-factor: "0.36"
emission-factor-source: "EPA eGRID 2025 - Region WECC"
utility-provider: "Pacific Gas & Electric"
Liabilities:CreditCard -350.00 USD
2026-03-17 * "Business flight SFO to ORD"
Expenses:Travel:Flights 420.00 USD
Assets:Carbon:Scope3:BusinessTravel 330.0 CO2e
miles-flown: "1846"
emission-factor: "0.179"
emission-factor-source: "DEFRA 2025 - Domestic flight"
flight-details: "United 1234"
Liabilities:CreditCard -420.00 USD
2026-03-10 * "Natural gas bill - heating"
Expenses:Utilities:Gas 125.00 USD
Assets:Carbon:Scope1:FacilityFuel 280.0 CO2e
therms-consumed: "42"
emission-factor: "6.67"
emission-factor-source: "EPA - Natural Gas"
Liabilities:CreditCard -125.00 USD
The metadata captures:
- Activity data (kWh, miles, therms) for activity-based methodology
- Emission factors (kg CO2e per unit) from authoritative sources
- Source documentation for audit verification
- Context for category classification
Queries for Emissions Reporting
Once the data is in, we can generate reports using Beancount’s query language:
/* Total emissions by scope for 2026 */
SELECT
account,
SUM(position) AS total_emissions
WHERE
account ~ '^Assets:Carbon:Scope[123]' AND
year = 2026
GROUP BY account
ORDER BY account;
/* Monthly Scope 2 trend analysis */
SELECT
year, month,
SUM(position) AS scope2_emissions
WHERE
account ~ '^Assets:Carbon:Scope2' AND
year = 2026
GROUP BY year, month
ORDER BY year, month;
Fava’s existing reporting infrastructure would display emissions alongside financial data.
Why This Matters
Single source of truth: Financial and carbon data live in one system, reducing reconciliation errors.
Audit trail: Every emission entry has transaction-level documentation, meeting verification requirements.
Version control: Git tracks who changed what emission factors when—critical for regulatory audits.
Historical data: When regulators ask for historical emissions, you have auditable records going back years.
Cost efficiency: No $50K/year software subscription. Just plain text files and Python.
Flexibility: Emission factors change, methodologies evolve—Beancount adapts without vendor lock-in.
Open Questions
I’m still working through some practical challenges:
-
Supplier-reported Scope 3 data: How do we structure transactions when suppliers provide their emissions data? Do we trust their numbers or verify independently?
-
Emission factor updates: The EPA and DEFRA update their emission factors annually. How do we handle historical data when factors change? Restate prior periods or maintain consistency?
-
Materiality thresholds: The GHG Protocol allows omitting immaterial categories. How do we document materiality decisions in Beancount?
-
Multi-location businesses: Different grid emission factors by location. Do we create sub-accounts per facility, or handle this in metadata?
-
Carbon offsets: How should we account for purchased carbon credits? As negative emissions in the same accounts, or separate offset accounts?
Has Anyone Tried This?
I’d love to hear from others who’ve experimented with environmental accounting in Beancount:
- What account structures have worked well?
- How do you handle the activity-based vs spend-based methodology split?
- Any custom Fava plugins for emissions dashboards?
- What emission factor databases do you use?
- How do you explain this approach to clients or auditors who expect traditional carbon accounting software?
For businesses facing ESG reporting requirements, this could be a game-changer. For the plain text accounting philosophy, it’s a validation that these principles extend far beyond traditional finance.
Looking forward to hearing your thoughts and experiences.