Published September 14, 2026 | Version v2026.9.0

Public Utility Data Liberation Project (PUDL) Data Release

Description

v2026.9.0 (2026-09-11)

This is a regular monthly PUDL data release, primarily motivated by updating the EIA-860M monthly data through August 2026. The biggest change this month is that we've started publishing a DuckDB version of the PUDL database! We will be deprecating the old pudl.sqlite database in January, 2027.

We also closed a gap where Pandera's Polars backend was silently skipping some of the schema validation checks declared in our metadata (thankfully they were being enforced independently elsewhere!). We've added several new tables (EIA-176 company characteristics, EIA-923 fuel stocks and energy storage, PHMSA distribution mains by install decade), and extended the new CEMS-derived operational characteristics analysis back to 2000. See below for all the details, and the linked PRs and issues for the full story.

Output Formats & Distribution

  • Added a fully processed ``pudl.duckdb`` database. After the ETL completes we now assemble all of the non-hourly PUDL tables into a single DuckDB database named pudl.duckdb alongside pudl.sqlite, both built directly from the Parquet outputs. It preserves the full set of column checks and primary key constraints. Foreign key constraints are currently omitted due to a handful of column type conflicts outlined in issue #5552 being addressed in PR #5554. Both databases are published to S3, GCS, and Zenodo along with our other outputs. See Data Access. See PR #5538.

  • The fully processed ``pudl.sqlite`` database is deprecated. PUDL's ETL no longer writes SQLite directly; pudl.sqlite is now built from the Parquet outputs after the ETL purely for backwards compatibility. We will stop producing SQLite versions of the fully processed PUDL data in 2027. Please migrate to the Parquet outputs or the new pudl.duckdb database. This deprecation does not affect the minimally processed raw FERC data, which will continue to be distributed as SQLite for the time being, but will likely transition at some point in the near future as well. See PR #5538.

New Data

EIA-176

  • Added core_eia176__yearly_company_characteristics with company operation type, ownership type, and company characteristic fields from EIA Form 176 Part 3 (Lines A-D). Includes alternative_fleet_size, customer_choice_residential_eligible, customer_choice_residential_participating, has_sales_or_acquisitions, and natural_gas_pump_price_dollars_per_mcf (2014-2016 only). National-level adjustment records (operating_state FX, MX, BL, OO) are excluded. The raw is_other_ownership and is_other_ownership_2 fields (which never co-occur) are merged into a single is_other_ownership boolean. See #4697 and #5412. Thank's to @MeadBarrel for all the work on getting natural gas data integrated into PUDL, and the ongoing support from @switchbox-data.

EIA-923

EPA CEMS

  • Added a new analysis output, out_epacems__yearly_operational_characteristics, which estimates generator operational characteristics such as minimum stable operating level, minimum up/down times, ramp rates, and heat rates at maximum and minimum load, inferred from hourly EPA CEMS gross load and fuel heat content data over a rolling three-year window. This dagsterizes and vectorizes an analysis originally developed by Sylvan Energy, making it available for all reporting states rather than just California. The output is experimental and marked accordingly, since we are soliciting feedback from the community on the underlying methodology. See issue #5106 and PR #5190.

  • out_epacems__yearly_operational_characteristics now reports these estimates for every calendar year with a full three-year trailing window of usable EPA CEMS data, rather than only the most recent year, going back to 2000 (EPA CEMS's first three reporting years, 1995-1997, are excluded due to known poor unit coverage). Also recalibrated the associated dbt data validations against physically grounded bounds (e.g. the 3.412 MMBtu/MWh thermodynamic floor on heat rates, and the exact trailing window length as an upper bound on minimum up/down times) rather than thresholds fit to a single year of data. See PR #5474.

PHMSA

  • Added the core_phmsagas__yearly_distribution_by_install_decade table, which reports PHMSA gas distribution mains miles and services by installation decade. See issue #5266 and PR #5443.

Expanded Data Coverage

EIA-191

  • Updated EIA-191 data to include additional records through June 2026. See PR #5572.

EIA-860M

EIA-861

EIA-930

  • Updated EIA-930 data to include data published up through the beginning of September 2026. See PR #5572.

EIA API Bulk Electricity Data

  • Updated the EIA Bulk Electricity data to include data published up through the beginning of June 2026. See PR #5572.

EPA CEMS

  • Updated the EPA CEMS data with additional records through June 2026. See PR #5572.

EPA MATS

  • Updated the raw EPA MATS archive with additional records through 2026 Q2 (June 2026). This data is not yet deeply integrated into PUDL. See PR #5572.

NREL ATB

  • Updated the NREL ATB extractor and transformer to accommodate changes to the 2024 data and format. See issue #5467 and PR #5513.

  • Added NREL ATB 2025 data. See PR #5569.

PHMSA

  • Added 2025 distribution and transmission data for PHMSA. See issue #5504 and #5548.

FERC EQR

FERC Form 2

  • Updated the raw FERC Form 2 archive to include additional 2025 data. This data is converted to SQLite, but not deeply integrated into PUDL. See PR #5572.

Vibrant Pattern Futures Resource Adequacy Renewable Energy (RARE) Power Dataset

New Data Tests & Validations

  • The subcomponents_sum_to_total dbt test can now identify subcomponents and totals by a combination of categorical columns (e.g. (cost_group, cost_type) tuples) rather than values from a single column, enabling validation of calculations that cross more than one categorical column. Used this to complete the totals validations for core_rus12__yearly_plant_costs and out_rus12__yearly_plant_costs, adding maintenance, operations & maintenance, fixed cost, and total power cost checks, and fixing several existing checks that referenced non-existent cost categories. The test also gained an opt-in minimum_total_coverage argument that catches totals which silently match no records at all (e.g. a misspelled total_label or a category renamed in a future data update), enabled for all of the plant costs checks. See issues #5378, #5154 and PR #5510.

  • Calibrated per-year error thresholds for the heat-rate outlier checks on out_epacems__yearly_operational_characteristics, since the multi-year extension in PR #5474 introduced report years whose data quality, and thus expected outlier rate, varies considerably over the 2000-2025 span. See PR #5571.

Bug Fixes & Data Cleaning

  • Fixed a bug in out_eia__yearly_generators_by_ownership where every ownership record for a jointly owned generator reported the plant operator's utility_id_pudl and utility_name_eia instead of the owner's. When ownership slices are generated, the owner's PUDL utility ID and EIA utility name are now swapped in alongside the owner's utility_id_eia. See issue #5430 and PR #5506.

  • Fixed set_gcs_temporary_hold only protecting the top level of a versioned release path from deletion. It shelled out to gcloud storage objects update gs://bucket/prefix/*, and that glob only matches one path segment, so anything nested in a subdirectory was silently left unheld. Updated the function to use the google-cloud-storage API to recursively hold every object under the prefix and verify after the fact that none were missed. Manually re-applied the hold to 4,000+ previously published versioned release objects that had been missed by the original bug. See PR #5477.

  • Fixed EIA-176 extraction bug where raw_eia176__operation_types_and_sector_items was always empty due to a mismatched page key. See #4697 and #5412.

  • Recovered dbt data validation tests that were being silently dropped from several tables as their source schema.human.yml files still used the deprecated tests: key instead of data_tests:, meaning dbt_helper silently discarded the tests they contained instead of merging them into the generated schema.yml. See PR #5458.

  • Fixed allocate_gen_fuel.py silently dropping legitimate generation and fuel data for generators transitioning between proposed/existing or existing/retired status across a multi-year ETL run. Unified the slightly different logics of these transitions into a single, shared, symmetric process. This simplification exposed a bug in which one generator's status transition could silently prevent data from another group of generators at the same plant from being allocated. Added extensive unit tests an new dbt data quality tests validating that >=99.7% of all reported generation and fuel survives allocation. Thanks to @grgmiller for surfacing and starting this fix. See #5440 and PRs #5419, #5511.

  • Fixed several sources of non-deterministic row counts, where identical code and data produced different results on different machines (e.g. local macOS vs. nightly Linux builds) because several functions resolved ties among candidate values using incidental pandas/numpy sort or dedup behavior instead of an explicit, deterministic rule. Affected tables now use the standard, exact check_row_counts_per_partition dbt test in place of the looser row-count range checks previously used to work around the instability. See #4574, #4254, and #5503.

  • Fixed add_null_overrides() in the FERC1-EIA record linkage nulling out the condensed report_date, report_year, plant_id_pudl, and utility_id_pudl columns for every known-unmatched FERC1 record, instead of only the EIA match columns. With the fix, 788 records in out_pudl__yearly_assn_eia_ferc1_plant_parts which used to have a NULL report_date now appear with correct date information. See issue #4130 and PR #5503

  • Improved the robustness of FERC EQR raw data extraction. Filings that are missing one or more of the expected CSV files, or whose company identity information is missing or unparsable, now have as much of their data extracted as possible instead of being dropped entirely. Parquet outputs for FERC EQR's constrained categorical columns also now preserve their full set of allowed values, matching the behavior of other PUDL categorical columns. See PR #5442.

  • Disabled primary key uniqueness enforcement on core_ferceqr__quarterly_index_pub, which was found to contain duplicate primary key values in FERC EQR filings from 2023 onward. See PR #5442.

  • Fixed a race condition that intermittently failed the docs build due to the HTML and Markdown builds attempting to clean up the same dynamically generated output files at the end of their build. Fixed by setting llms_txt_build_parallel = False. See issue #5502 and PR #5516.

  • Changed subplant_id in core_epa__assn_eia_epacamd_subplant_ids to be 1-indexed instead of 0-indexed within each plant_id_eia, so the first subplant at a plant is now 1 rather than 0. See issue #5499 and PR #5541.

  • Retired the interim output _core_phmsagas__yearly_distribution_by_install_decade, which was replaced by the cleaned and validated core_phmsagas__yearly_distribution_by_install_decade. See #5504 and #5548.

  • Fixed the DuckDB examples in Data Access and the per-table access snippets in the data dictionary. Because our S3 bucket name contains dots, DuckDB's default virtual-host addressing hit a TLS certificate mismatch; the examples now create an anonymous path-style S3 secret (CREATE SECRET (TYPE s3, PROVIDER config, REGION 'us-west-2', URL_STYLE 'path')) before querying. See PR #5538.

  • Added a data validation test that checks pudl.sqlite, pudl.duckdb, and the Parquet outputs are mutually consistent: every table defined in PUDL_PACKAGE is present in both databases, neither database has extra tables, and every table has the same columns and the same row count in SQLite, DuckDB, and its source Parquet file. See PR #5538.

  • Closed a long-standing gap in which Pandera's Polars backend only checked column presence and dtype for pl.LazyFrame assets, silently skipping every range, enum, nullability, regex, and uniqueness check declared in our metadata for the vast majority of PUDL tables. Content validation is now explicitly enabled for these assets, checked one column at a time to keep memory bounded even on PUDL's largest tables. Thankfully these schema checks were also being enforced through independent mechanisms, so no data quality issues resulted from this gap. See PR #5432.

  • Corrected the last_annual_meeting_date field in core_rus7__yearly_meeting_and_board and core_rus12__yearly_meeting_and_board from a datetime to a date type. See PR #5518.

Performance Improvements

  • Fixed a performance regression resulting from the update to Numpy 2.5, which ended up using complex arithmetic in calculating eigenvalues due to floating point noise in the imaginary components of the matrix math we were doing in our timeseries imputations. See PR #5503.

Developer Experience

  • Added ferceqr_pipeline_diagnostics, a metadata-only Dagster asset that compiles the extraction stats and schema-check results already recorded for every FERC EQR quarter into one wide table, so anomalies like missing tables, rejected records, or primary-key violations are visible across the whole backfill without opening each quarter's materialization individually. See PR #5457.

  • Reworked how the Google Batch jobs launched by the build-pudl, deploy-pudl, and build-deploy-ferceqr GitHub workflows are configured. The Batch config generator moved from devtools/ into the package as the batch_config script (pudl.scripts.batch_config); the workflows now select an explicit VM machine type and boot disk rather than passing raw resource counts, and every VM and its logs are tagged with the pipeline that launched them so a shared Cloud Monitoring dashboard can filter resource-usage metrics by pipeline. VM sizes and the ETL's process and thread parallelism were tuned to match measured resource usage and stop oversubscribing the CPUs. See #5545.

  • Branch builds (build-pudl runs triggered via workflow_dispatch) now skip the S3 deployment by default and only deploy to GCS. S3 egress fees cost more than a full ETL run, and the nightly build already exercises the real S3 deployment every night. The build-pudl and deploy-pudl workflow-dispatch forms expose deploy_to_gcs / deploy_to_s3 checkboxes to override this per run, and when neither target is enabled build-pudl skips triggering deploy-pudl altogether (e.g. a build run only to regenerate row counts). Nightly and stable deployments are unchanged and still deploy to both. See issue #5557 and PR #5558.

  • Fixed several issues with how dbt_helper update-tables renders schema.yml (pudl.dbt_schema): long description: fields are now wrapped into readable paragraph blocks and strings that need quoting prefer double quotes. This now matches Prettier's YAML conventions, minimizing the need for reformatting after generation. Standardized multi-line description: fields across all schema.human.yml inputs. dbt_helper update-tables --schema --clobber all is now idempotent across all tables. See PR #5458.

  • Pydantic models representing dbt structures defined in pudl.dbt_schema now reject any unrecognized keys (like stray tests: instead of data_tests:) at parse time instead of silently discarding them. Whitespace in description fields is also normalized at parse-time to avoid spurious diffs. See PR #5458.

  • Reorganized tests/ into four tiers by run-time: fast unit tests run as a pre-commit hook, slower integration tests run on every push, and slower ETL-dependent pipeline and data validation tests run in the merge queue. Previously tests/integration mixed ETL and integration tests together, so many lightweight tests weren't getting run until the merge queue, leading to unexpected late-stage failures. A pytest collection hook enforces the ETL/no-ETL split. Also fixed a live Zulip notification firing from the test suite and tightened the dbt schema.yml round-trip test. See issue #5508 and PR #5507.

  • Validate foreign key constraints with dbt instead of SQLite. Update our dbt_helper script to autogenerate FK constraint tests based on the PUDL metadata. Remove the SQLite based FK checking infrastructure. Also add sensible defaults for our row-count expectation checking test so we can remove boilerplate test specs. See issues #4564, #5208 and PR #5519.

  • Automated updating the Zenodo deposition metadata (creators, keywords, version, description, and structured resource links) for monthly PUDL data releases, which previously had to be hand-edited in the Zenodo web UI every month. Creators and keywords are now read from .zenodo.json, and the description is assembled from the built release notes for that version plus a footer of release-specific resource links (versioned docs, data dictionary, S3/GCS paths, the GitHub release, and the corresponding GitHub-repo Zenodo software archive), which are also populated as structured related_identifiers for better DataCite/OpenAIRE indexing. See issue #3326 and PR #5484.

  • Removed Alembic and the PUDL SQLite schema migrations. With PUDL's own tables no longer written to SQLite during the ETL, there is no schema for Alembic to manage, so alembic.ini, the migrations/ directory, and the alembic dependency have been removed. See PR #5538.

  • Replaced the pudl_engine pytest fixture (a SQLAlchemy engine) with pudl_sqlite_connection alongside a pudl_duckdb_connection. Both of which are DuckDB connections. One dedicated to reading pudl.sqlite via DuckDB's sqlite extension, so tests query both build outputs through one API as PUDL moves toward DuckDB. See PR #5538.

Other PUDL v2026.9.0 Resources

Contact Us

If you're using PUDL, we would love to hear from you! Even if it's just a note to let us know that you exist, and how you're using the software or data. Here's a bunch of different ways to get in touch:

Files

censusdp1tract.sqlite.zip

Files (29.3 GB)

Name Size
md5:010b3bebc45368daff64ec29e5e66c19
506.7 MB Preview Download
md5:8babe02989ba36a9e2428d42f28656a2
326.6 kB Preview Download
md5:d6e3cb91ea3425a6cf30bd4ebce66357
275.0 MB Preview Download
md5:5f500f671f0cf8c8700e69f832e36868
202.9 MB Preview Download
md5:7e864459106af44bdc1bedcb98504361
274.6 kB Preview Download
md5:be0bd99028f043183af81218d2b12b77
1.2 GB Download
md5:2265e62412c2705b24f1964672fa54e9
240.0 MB Preview Download
md5:78af411ab7f09221b83af9cce1f66864
202.7 MB Preview Download
md5:81ebc5ff24eacea319bc707ee947b3d0
2.3 MB Preview Download
md5:ee5c7f18d569c58f3f008b96a16bedff
8.5 MB Preview Download
md5:52607e2e7cd29e7c99eeea92d28c998f
74.7 MB Preview Download
md5:e046b96eb98b681d648aa07872168925
48.3 MB Preview Download
md5:e3e61cc19e85ede64ab84de4099bf259
221.2 kB Preview Download
md5:2570028a0b706af48666b5d94071ebba
148.4 MB Download
md5:b7a8611cc017af166b9e715becbb1c2d
36.0 MB Preview Download
md5:8acf59c6c13dd283766f8029e11f3da7
23.3 MB Preview Download
md5:789c4f018b6f33df78ef9b02c7406b22
2.3 MB Preview Download
md5:bd09d11e1a79a1623b3d9789bf336c9e
7.2 MB Preview Download
md5:148635a16a7da3113334cb4f435614cd
2.9 MB Preview Download
md5:fadc2713d87217de9eba80a955eddb6c
2.8 MB Preview Download
md5:90a7386f6ee4339e182d22585f3ef79d
72.2 kB Preview Download
md5:1566175732ed2c769b16806227be8230
50.9 MB Download
md5:75c24b26e7cec6bcc8b57bfd0b9fe13b
5.4 MB Preview Download
md5:07237fda65bc9e0c9b1164f1099cbe27
6.6 MB Preview Download
md5:ce7a444b26f2602690f06cf7b51eac96
978.1 kB Preview Download
md5:ba679325b7ae96c87b6fa5584cfae491
2.0 MB Preview Download
md5:61d84218a3fbf15e7b87b8571bef72ec
44.0 MB Preview Download
md5:412d757b9319bc13a76dbe16d3013e32
27.2 MB Preview Download
md5:ca7d402847ca8025450e1c0372f7fb2a
127.9 kB Preview Download
md5:099d506cf3ea2852cf2587e8109b9652
72.1 MB Download
md5:355c0fff0066df72b4c970db7108be71
29.1 MB Preview Download
md5:8e456fe699fb7483fdd6ef85c1bbaabc
15.1 MB Preview Download
md5:14ceae80aa913ecd6829560f70a0fe5a
1.3 MB Preview Download
md5:240ab648e9b6fb13f2540b217638676e
3.0 MB Preview Download
md5:a08a470a665f97b4df90c8b7f1ad1332
81.3 MB Download
md5:306b3d2e38ccaff29815b217a58e5da2
243.1 MB Preview Download
md5:4347eee6d05389e9a1519e068a3f2ee1
43.4 MB Preview Download
md5:41dddcbe79eb7979816489687745822d
65.7 kB Preview Download
md5:83bcf37029395440c6a0efb7ce49ee8e
192.9 kB Preview Download
md5:b5938aa070a7614ac5b5a0e582992124
38 Bytes Download
md5:df55826c785cd4b96d7097d9f39cb0b1
9.5 GB Download
md5:966f053858bd4f826198a154a2b4d975
3.5 GB Preview Download
md5:ed5568bc112a56989aa6634dea2498a0
12.7 GB Preview Download
md5:fbac51ecf7b9f94db2c4a081ea7bc3f3
4.5 MB Preview Download

Additional details