I've been building energy APIs for about four years. The thing that kept bothering me wasn't the code — it was watching tools I respected give completely wrong BESS cost estimates because they multiplied system size by a flat dollars-per-kilowatt-hour figure.
That's not how battery storage costs work. And it matters a lot when someone is deciding whether to spend $800,000 on a system.
The problem with flat $/kWh
Most BESS calculators I've seen do something like this:
# What most tools do — this is wrong
system_cost = capacity_kwh * cost_per_kwh # e.g. 500 kWh * $400 = $200,000
The issue is that battery storage has two fundamentally different cost components that scale differently:
- Energy cost — scales with kWh (how much you can store)
- Power cost — scales with kW (how fast you can charge/discharge)
A 500 kWh system with a 250 kW inverter has a completely different cost structure than a 500 kWh system with a 500 kW inverter. The flat $/kWh model treats them identically.
The NREL ATB two-component model.
NREL's Annual Technology Baseline (ATB) 2024 separates these correctly:
Total Cost ($/kW) = [BatteryPack ($/kWh) × Duration (h)] + BOS ($/kW)
Where BOS (balance of system) covers the inverter, controls, and
installation — costs that scale with power capacity, not energy capacity.
For LFP at moderate scenario (2024 values from Cole, Ramasamy & Turan 2025, NREL/TP-6A40-93281):
# The correct model
pack_cost_per_kwh = 241 # $/kWh — energy component
bos_cost_per_kw = 339 # $/kW — power component
duration_hours = 4.0 # hours
def calculate_bess_capex(power_kw: float, duration_h: float) -> dict:
energy_kwh = power_kw * duration_h
energy_cost = energy_kwh * pack_cost_per_kwh
power_cost = power_kw * bos_cost_per_kw
total_cost = energy_cost + power_cost
cost_per_kwh = total_cost / energy_kwh # effective $/kWh
return {
"energy_kwh": energy_kwh,
"energy_cost_usd": energy_cost,
"power_cost_usd": power_cost,
"total_cost_usd": total_cost,
"effective_per_kwh": cost_per_kwh, # varies with duration!
}
# 500 kW, 4-hour system
result = calculate_bess_capex(500, 4.0)
# total: $920,500 | effective: $460/kWh
# Same energy, 2-hour system (1000 kW inverter)
result2 = calculate_bess_capex(1000, 2.0)
# total: $1,143,000 | effective: $571/kWh
Same total energy stored. 24% cost difference. That's the error you make with flat $/kWh.
Why duration changes the effective cost per kWh
Here's the relationship visualized:
The longer the discharge duration, the lower the effective $/kWh — because you're spreading the fixed power costs (BOS) over more stored energy. This is why 6-8 hour systems often pencil out better for large industrial loads even though the upfront cost is higher.
Round-trip efficiency convention — another source of errors
One more thing that trips people up: manufacturer specs often quote DC-DC round-trip efficiency (0.92–0.95 for LFP). But commercial buildings are billed on AC metered output, so you need the AC-AC figure.
# DC-DC (what manufacturers advertise)
rte_dc_dc = 0.93
# AC-AC (what actually matters for your electricity bill)
rte_ac_ac = 0.85 # LFP — from NREL ATB 2024b and PNNL-33283
# The difference matters for savings calculations
annual_cycles = 365
usable_kwh = 400
dc_dc_throughput = annual_cycles * usable_kwh * rte_dc_dc # 136,380 kWh
ac_ac_throughput = annual_cycles * usable_kwh * rte_ac_ac # 124,100 kWh
# At $0.18/kWh, that's a $2,207/year difference in projected savings
What I built
I wrapped all of this into a FastAPI service that also handles:
- Load Duration Curve sizing (Neubauer & Simpson 2015, NREL/TP-5400-63162)
- LCOS calculation per PNNL-33283
- IRA 2022 §48E ITC stacking (base 30% + up to 20% in adders)
- Demand charge savings with state-level rates from OpenEI
Every output includes the source equation and the paper it comes from. The API is live on RapidAPI if you want to try it.
Sources: Cole, Ramasamy & Turan (2025) NREL/TP-6A40-93281 ·
Mongird et al. (2022) PNNL-33283 · Neubauer & Simpson (2015)
NREL/TP-5400-63162 · IRA 2022 §48E

Top comments (0)