ERCOT market data in one line of Python — DAM/RTM prices, LMPs, load, generation, and more.
Project description
ercot
ERCOT market data in one line of Python.
A thin SDK for the ERCOT Public API that gives you Day-Ahead and Real-Time settlement point prices, LMPs, load data, generation mix, and more — all as pandas DataFrames with proper Central Prevailing Time DST handling and one-call Excel/CSV export.
import ercot as er
er.configure("you@email.com", "your-password", "your-subscription-key")
df = er.dam("LZ_HOUSTON", "2025-07-01", "2025-07-31")
er.save_xlsx(df, "houston_dam_july.xlsx")
Installation
pip install ercot
Getting Your ERCOT API Credentials
You need three things: a username (email), a password, and a subscription key. All free. Here's exactly how to get them.
Step 1: Register an Account
- Go to https://apiexplorer.ercot.com/
- Click Sign Up in the top-right corner
- Fill in your email address and create a password
- Complete the email verification (check your inbox for the confirmation link)
- Once verified, sign in to the API Explorer
Step 2: Subscribe to the Public API
- After signing in, click Products in the top navigation
- Click Public API to open the product page
- Enter a name for your subscription (e.g., "my-ercot-app") in the Subscription name field
- Click Subscribe
- Wait for your subscription to be approved (usually instant, but can take up to a few hours)
Step 3: Get Your Subscription Key
- After approval, click your username in the top-right corner → Profile
- You'll see your subscription(s) listed
- Click Show next to the Primary key field
- Copy this key — this is your subscription key (also called
Ocp-Apim-Subscription-Key)
Step 4: Configure ercot
import ercot as er
# This saves your credentials to ~/.ercot/credentials.json
# so you only need to do it once.
er.configure(
"you@email.com", # the email you registered with
"your-password", # the password you created
"your-subscription-key", # the Primary key from Step 3
)
That's it. All subsequent calls will use the saved credentials automatically.
Alternative: Environment Variables
If you prefer not to save credentials to disk (e.g., in CI/CD or Docker):
export ERCOT_API_USERNAME="you@email.com"
export ERCOT_API_PASSWORD="your-password"
export ERCOT_API_SUBSCRIPTION_KEY="your-subscription-key"
Alternative: Pass Directly
c = er.client(
username="you@email.com",
password="your-password",
subscription_key="your-key",
)
df = c.dam_spp("2025-07-01")
Quick Start
import ercot as er
# Day-Ahead prices — wide format (date + HE1..HE24 columns)
df = er.dam("LZ_HOUSTON", "2025-07-01", "2025-07-31")
# Real-Time prices — long format (one row per 15-min interval)
df = er.rtm("HB_NORTH", "2025-07-01", "2025-07-07", format="long")
# All load zones at once
df = er.dam("zones", "2025-01-01", "2025-01-31")
# Save to Excel (styled headers, alternating rows)
er.save_xlsx(df, "prices.xlsx")
# Save to CSV
er.save_csv(df, "prices.csv")
Output Formats
Wide Format (default)
One row per date per settlement point. Interval values are columns.
DAM (24 hourly intervals):
| date | settlement_point | HE1 | HE2 | HE3 | ... | HE24 |
|---|---|---|---|---|---|---|
| 2025-07-01 | LZ_HOUSTON | 28.54 | 25.12 | 23.87 | ... | 38.91 |
| 2025-07-01 | LZ_NORTH | 27.10 | 24.03 | 22.95 | ... | 37.44 |
RTM (96 fifteen-minute intervals):
| date | settlement_point | IE1 | IE2 | ... | IE96 |
|---|---|---|---|---|---|
| 2025-07-01 | LZ_HOUSTON | 24.12 | 25.67 | ... | 35.44 |
Long Format
One row per interval with a CPT-aware interval_ending datetime.
DAM:
| date | settlement_point | hour | interval_ending | price |
|---|---|---|---|---|
| 2025-07-01 | LZ_HOUSTON | 1 | 2025-07-01 01:00:00-05:00 | 28.54 |
| 2025-07-01 | LZ_HOUSTON | 2 | 2025-07-01 02:00:00-05:00 | 25.12 |
RTM:
| date | settlement_point | interval | interval_ending | price |
|---|---|---|---|---|
| 2025-07-01 | LZ_HOUSTON | 1 | 2025-07-01 00:15:00-05:00 | 24.12 |
| 2025-07-01 | LZ_HOUSTON | 2 | 2025-07-01 00:30:00-05:00 | 25.67 |
DST Handling
ERCOT operates in Central Prevailing Time (CPT). ercot handles DST transitions correctly:
- Spring forward (March): Hour Ending 03:00 is skipped. DAM has 23 intervals; RTM has 92.
- Fall back (November): Hour Ending 02:00 repeats. ERCOT flags the second occurrence. DAM has 25 intervals; RTM has 100.
In long format, the interval_ending column is a timezone-aware timestamp (America/Chicago) with the correct UTC offset for each row — CDT intervals show -05:00, CST intervals show -06:00.
Settlement Point Shortcuts
| Shortcut | Expands To |
|---|---|
'LZ_HOUSTON' |
Single load zone |
['LZ_HOUSTON', 'HB_NORTH'] |
Specific list |
'zones' |
All 8 load zones: LZ_HOUSTON, LZ_NORTH, LZ_SOUTH, LZ_WEST, LZ_AEN, LZ_CPS, LZ_LCRA, LZ_RAYBN |
'hubs' |
All 6 trading hubs: HB_BUSAVG, HB_HOUSTON, HB_NORTH, HB_SOUTH, HB_WEST, HB_PAN |
'all' |
All zones + hubs combined |
Full API Reference
Prices
| Function | Description |
|---|---|
er.dam(points, start, end, format=) |
DAM Settlement Point Prices (hourly) |
er.rtm(points, start, end, format=) |
RTM Settlement Point Prices (15-min) |
er.dam_lmp(start, end, points) |
DAM Hourly LMPs |
er.rtm_lmp(start, end, points) |
RTM LMPs by Settlement Point (5-min) |
Load
| Function | Description |
|---|---|
er.system_load(start, end) |
Actual system load by weather zone |
er.load_forecast(start, end) |
7-day load forecast by model/weather zone |
Generation
| Function | Description |
|---|---|
er.wind_production(start, end) |
Wind power — actual and forecast |
er.solar_production(start, end) |
Solar power — actual and forecast |
er.fuel_mix(start, end) |
Generation by fuel type |
Grid / Constraints
| Function | Description |
|---|---|
er.dam_shadow_prices(start, end) |
DAM shadow prices and binding constraints |
er.sced_shadow_prices(start, end) |
SCED shadow prices and binding constraints |
Reference
| Function | Description |
|---|---|
er.settlement_points_list() |
All settlement points and bus mapping |
er.list_endpoints() |
List all available endpoints |
Export
| Function | Description |
|---|---|
er.save_xlsx(df, "file.xlsx") |
Formatted Excel (styled headers, alternating rows) |
er.save_csv(df, "file.csv") |
Standard CSV export |
Advanced
# Get the full client for custom queries
c = er.client()
df = c.request("fuel_mix", {"deliveryDateFrom": "2025-07-01"})
# Hit an endpoint not in the registry
df = c.raw("/np6-xxx-cd/some_endpoint", {"param": "value"})
Excel Export Features
er.save_xlsx() produces publication-quality spreadsheets:
- Header row: Dark teal background, white bold text, centered, with borders
- Data rows: Alternating white / light gray for readability
- Price columns: Automatically detected (HE*, IE*, price) and formatted as
$#,##0.00 - Column widths: Auto-fitted to content
- Frozen panes: Header row stays visible when scrolling
- Auto-filter: Filter dropdowns on every column
- Optional high-price highlighting: Red background for prices above a threshold
# Basic
er.save_xlsx(df, "prices.xlsx")
# Multi-sheet workbook
er.save_xlsx({
"DAM Houston": dam_df,
"RTM Houston": rtm_df,
"Load": load_df,
}, "ercot_report.xlsx")
# Highlight extreme prices
er.save_xlsx(df, "prices.xlsx", highlight_high_prices=True, high_price_threshold=200)
Architecture
ercot/
├── __init__.py # Public API — er.dam(), er.rtm(), er.save_xlsx(), etc.
├── models.py # Constants, enums, endpoint registry, timezone
├── auth.py # OAuth2 ROPC token management, credential storage
├── client.py # ErcotClient — one method per endpoint, pagination
├── formatting.py # Wide/long conversion, DST-aware datetimes
└── export.py # CSV and formatted XLSX output
Data flow: ERCOT API → ErcotClient → DataFrame → (optional) formatting → (optional) export
No local database, no caching, no stale data. Every call hits the live API.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ercot-0.1.1.tar.gz.
File metadata
- Download URL: ercot-0.1.1.tar.gz
- Upload date:
- Size: 23.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5edf3f4cbf46d51befe7ef0a30d82202cce39c2e6684f7bbcdfd706f4f659165
|
|
| MD5 |
9777933642186dad49ec4a43e7258947
|
|
| BLAKE2b-256 |
5caafc66e14fce98b8b4792d2f5f1ad3349f7b795b8bfe76dc0c5ebbc0c2cbcd
|
File details
Details for the file ercot-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ercot-0.1.1-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00795bd83bf2931b48a5338bf6894b73eacaea1e57821393201aef191ad1f746
|
|
| MD5 |
df488cc6f7ef09b3d1390cf273617885
|
|
| BLAKE2b-256 |
5b02b1e4330eac8666835cd4e498976f119445693b9cecdcf2eb0df9df261a2f
|