python-eia: Descarga datos energéticos de EE.UU. en pocas líneas de Python
Una biblioteca de Python que envuelve la API v2 de EIA en llamadas simples — obtén mix de generación, demanda, intercambios y precios de gas natural como DataFrames.
If you’ve worked with US energy data, you’ve probably dealt with the EIA API — building URLs by hand, parsing JSON responses, handling pagination, and converting everything into DataFrames yourself.
We built python-eia to skip all of that. Discover what data is available, inspect filtering options, and download DataFrames — all from Python.
from eia import EIAClient
client = EIAClient() # reads EIA_API_KEY from environment
route = client.get_data_endpoint("electricity/rto/fuel-type-data")
route.facets.respondent.get_values() # discover available grid operators
df = route.get(facets={"respondent": "CISO"}, frequency="hourly", start="2024-06-01", end="2024-06-08") In this article, we’ll walk through the library’s main features with executable examples and Plotly visualizations. If you want to understand the raw API before using the library, read our EIA API tutorial.
Installation
pip install python-eia You’ll need a free API key from the EIA website. Set it as an environment variable:
export EIA_API_KEY=your-token-here Before vs after: the “aha” moment
If you’ve searched for a Python EIA library, you’ve probably found eiapy — the most downloaded option on PyPI. The problem? It targets API v1, which EIA shut down. It no longer works.
# Option 1: eiapy (targets deprecated API v1 — broken)
from eiapy import Series
cal_to_mex = Series('EBA.CISO-CFE.ID.H')
cal_to_mex.last(5) # ❌ EIA API v1 was shut down So you’re left writing raw requests against v2:
# Option 2: raw requests (works, but verbose)
import requests, pandas as pd
url = "https://api.eia.gov/v2/electricity/rto/fuel-type-data/data/"
params = {
"api_key": API_KEY,
"frequency": "hourly",
"data[0]": "value",
"facets[respondent][]": "CISO",
"start": "2024-06-01",
"end": "2024-06-08",
"length": 5000,
}
response = requests.get(url, params=params)
data = response.json()["response"]["data"]
df = pd.DataFrame(data)
# Still need: pagination, type coercion, error handling... With python-eia, the same task becomes:
# Option 3: python-eia (API v2, returns DataFrames)
from eia import EIAClient
client = EIAClient()
route = client.get_data_endpoint("electricity/rto/fuel-type-data")
df = route.get(facets={"respondent": "CISO"}, frequency="hourly", start="2024-06-01", end="2024-06-08") Key differences:
- API v2 — the only version that works today
- Data discovery — explore available datasets, facets, and frequencies from Python
- Automatic pagination — fetches all pages transparently
- Returns DataFrames — no JSON parsing, no type coercion
Discovering data from Python
The EIA API organizes data as a tree. With python-eia, you navigate it interactively — no need to check the website or guess route strings.
Step 1: Browse top-level categories
client.route("").keys() Out14 lines
['coal',
'crude_oil_imports',
'electricity',
'international',
'natural_gas',
'nuclear_outages',
'petroleum',
'seds',
'steo',
'densified_biomass',
'total_energy',
'aeo',
'ieo',
'co2_emissions']Step 2: Drill into a category
client.route("electricity").keys() Out6 lines
['retail_sales',
'electric_power_operational_data',
'rto',
'state_electricity_profiles',
'operating_generator_capacity',
'facility_fuel']client.route("electricity/rto").keys() Out8 lines
['region_data',
'fuel_type_data',
'region_sub_ba_data',
'interchange_data',
'daily_region_data',
'daily_region_sub_ba_data',
'daily_fuel_type_data',
'daily_interchange_data']Step 3: Reach a data endpoint
When keys() returns an empty list, you’ve reached a data endpoint. Access it with .data:
route = client.get_data_endpoint("electricity/rto/fuel-type-data") Step 4: Discover filtering options (facets)
Facets are the filters you can apply to a query. Each endpoint has different ones:
route.facets FacetContainer(facets=[respondent, fueltype])What values does each facet accept? Ask it:
route.facets.respondent.get_values()[:8] Out8 lines
[FacetValue(id='CISO', name='California Independent System Operator'),
FacetValue(id='CAR', name='Carolinas'),
FacetValue(id='TEPC', name='Tucson Electric Power'),
FacetValue(id='MIDA', name='Mid-Atlantic'),
FacetValue(id='SCEG', name='Dominion Energy South Carolina, Inc.'),
FacetValue(id='NY', name='New York'),
FacetValue(id='NYIS', name='New York Independent System Operator'),
FacetValue(id='NWMT', name='NorthWestern Corporation')]route.facets.fueltype.get_values() Out20 lines
[FacetValue(id='PS', name='Pumped storage'),
FacetValue(id='UES', name='Unknown energy storage'),
FacetValue(id='NUC', name='Nuclear'),
FacetValue(id='OTH', name='Other'),
FacetValue(id='NG', name='Natural Gas'),
FacetValue(id='WND', name='Wind'),
FacetValue(id='WNB', name='Wind with integrated battery storage'),
FacetValue(id='BAT', name='Battery'),
FacetValue(id='SUN', name='Solar'),
FacetValue(id='OIL', name='Petroleum'),
FacetValue(id='UES', name='Unknown Energy'),
FacetValue(id='GEO', name='Geothermal'),
FacetValue(id='SNB', name='Solar with integrated battery storage'),
FacetValue(id='WAT', name='Hydro'),
FacetValue(id='BAT', name='Battery storage'),
FacetValue(id='OES', name='Other energy storage'),
FacetValue(id='SNB', name='Solar Battery'),
FacetValue(id='COL', name='Coal'),
FacetValue(id='UNK', name='Unknown'),
FacetValue(id='PS', name='Pumped Storage')]Now you know: this endpoint has data for 80+ grid operators, broken down by 18 fuel types including Solar, Wind, Battery, and Nuclear.
Step 5: Check available frequencies
route.frequencies [FrequencyInfo(id='hourly', description='One data point for each hour in UTC time.', query='H', format='YYYY-MM-DD"T"HH24'),
FrequencyInfo(id='local-hourly', description='One data point for each hour in local time.', query='LH', format='YYYY-MM-DD"T"HH24TZH')]Step 6: Query
Now you have everything — no guessing, no documentation lookup:
df = route.get(
facets={"respondent": "CISO"},
frequency="hourly",
start="2024-06-03",
end="2024-06-06",
)
df.head()
This entire workflow — from “what data exists?” to a DataFrame — happens without leaving Python.
Use case 1: California’s energy mix
Let’s use the data we just discovered to visualize California’s generation by fuel type.

The solar bell curve is unmistakable — generation peaks at midday and drops to zero at sunset, when natural gas and imports ramp up.
Solar vs wind patterns

Solar follows a predictable bell curve while wind picks up at night — a natural complementarity that California relies on.
Use case 2: Regional demand comparison
Let’s discover a different endpoint. The region-data route has demand, forecasts, and net generation:
demand_route = client.get_data_endpoint("electricity/rto/region-data")
demand_route.facets FacetContainer(facets=[respondent, type])demand_route.facets.type.get_values() [FacetValue(id='TI', name='Total interchange'),
FacetValue(id='NG', name='Net generation'),
FacetValue(id='D', name='Demand'),
FacetValue(id='DF', name='Day-ahead demand forecast')]Type D is demand, DF is day-ahead forecast. Let’s compare demand across the four largest grid operators:
df_demand = demand_route.get(
facets={"respondent": ["CISO", "PJM", "ERCO", "MISO"], "type": "D"},
frequency="hourly",
start="2024-06-03",
end="2024-06-05",
)
df_demand.head()

PJM (the Mid-Atlantic region) consistently shows the highest demand, while ERCOT (Texas) shows the sharpest peaks — driven by air conditioning during summer heat.
Demand vs forecast accuracy
Since we discovered that type has both D (actual) and DF (forecast), we can compare them:
df_actual_forecast = demand_route.get(
facets={"respondent": "CISO", "type": ["D", "DF"]},
frequency="hourly",
start="2024-06-01",
end="2024-06-08",
) 
CAISO’s demand forecasts track reality closely — forecast errors typically stay within a few percent.
Use case 3: Interstate electricity flows
The interchange endpoint shows electricity flowing between grid operators:
interchange_route = client.get_data_endpoint("electricity/rto/interchange-data")
interchange_route.facets FacetContainer(facets=[fromba, toba])df_interchange = interchange_route.get(
facets={"fromba": "CISO"},
frequency="hourly",
start="2024-06-03",
end="2024-06-04",
)
df_interchange.head()

Positive values mean California is exporting; negative values mean importing. The midday solar surplus often gets exported to neighboring grids.
Use case 4: Natural gas prices
The EIA API isn’t just electricity — let’s navigate to natural gas:
client.route("natural-gas").keys() ['sum', 'pri', 'enr', 'prod', 'move', 'stor', 'cons']client.route("natural-gas/pri").keys() ['sum', 'fut', 'rescom']gas_route = client.get_data_endpoint("natural-gas/pri/fut")
gas_route.facets FacetContainer(facets=[duoarea, product, process, series])gas_route.facets.series.get_values()[:5] Out5 lines
[FacetValue(id='RNGWHHD', name='Henry Hub Natural Gas Spot Price (Dollars per Million Btu)'),
FacetValue(id='RNGC3', name='Natural Gas Futures Contract 3 (Dollars per Million Btu)'),
FacetValue(id='RNGC2', name='Natural Gas Futures Contract 2 (Dollars per Million Btu)'),
FacetValue(id='RNGC4', name='Natural Gas Futures Contract 4 (Dollars per Million Btu)'),
FacetValue(id='RNGC1', name='Natural Gas Futures Contract 1 (Dollars per Million Btu)')]RNGWHHD is the Henry Hub spot price — the benchmark for US natural gas:
df_gas = gas_route.get(
facets={"series": "RNGWHHD"},
frequency="daily",
start="2024-01-01",
end="2024-12-31",
data_columns=["value"],
)
df_gas.head()


Winter months show higher prices and more volatility — driven by heating demand and weather uncertainty.
Conclusions
python-eia simplifies access to US energy data:
- Data discovery — browse categories, endpoints, facets, and valid filter values without leaving Python
- API v2 native — the only version that works since EIA deprecated v1
- Automatic pagination — large queries are fetched transparently across multiple pages
- Returns DataFrames — no JSON parsing needed, ready for analysis
Resources
- PyPI: python-eia
- GitHub: datons/python-eia
- Deep dive: If you want to understand the raw API before using the library, read our EIA API in Python tutorial
- European energy data? Check out python-entsoe — our sister library for the ENTSO-E Transparency Platform