Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v.Next

### Packaging

- Ship a `py.typed` marker (PEP 561) so downstream type checkers consume the package's type hints (#110)

## 0.16.0 - July 7, 2026

### New Features
Expand Down
157 changes: 75 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,34 @@
</a>
</p>

# GridStatus.io Hosted API
# GridStatus.io Hosted API — Python Client

* Python client for accessing the [GridStatus.io Hosted API](https://www.gridstatus.io/api).
* Browse all available datasets in our [Data Catalog](https://www.gridstatus.io/datasets).
`gridstatusio` is a Python client for the [GridStatus.io Hosted API](https://www.gridstatus.io/api), which provides historical and real-time electricity market data from North American ISOs through a single REST API.

Available datasets cover load and demand, fuel and generation mix, forecasts, locational marginal prices (LMPs), interchange, ancillary services, and more. Browse 500+ datasets in the [Data Catalog](https://www.gridstatus.io/datasets).

## Contents

- [Why the hosted API?](#why-the-hosted-api)
- [Installation](#installation)
- [Getting started](#getting-started)
- [Return formats](#return-formats)
- [API usage](#checking-your-api-usage)
- [More examples](#more-examples)

## Why the hosted API?

The hosted API differs from the open-source [`gridstatus`](https://github.com/gridstatus/gridstatus) library in several ways:

| Hosted API | Open-source `gridstatus` |
|------------|--------------------------|
| Consistent column names, timestamp formats, and DST handling | Raw data directly from ISO sources |
| Single REST API | Source-specific integrations |
| Historical data queryable immediately | Historical availability depends on each source's retention policy |
| Consistent server-side filtering by time, columns, and row values | Filtering capabilities vary by source |
| Support included with paid subscriptions | Community support |

Use the open-source library when you want raw data directly from the ISOs with no account. We recommend this client when you want normalized, hosted data through a single API.

## Installation

Expand All @@ -34,22 +57,31 @@ uv pip install gridstatusio[notebooks]
uv pip install gridstatusio[all]
```

## Getting Started
## Getting started

* Sign up for a Grid Status account and get your API key from the [Settings page](https://www.gridstatus.io/settings/api)
* Set your API key as an environment variable: `export GRIDSTATUS_API_KEY=your_api_key` or pass to the client with `client = GridStatusClient(api_key="<your_api_key>")`
* You're now ready to start querying. List datasets with:
1. **Get an API key.** Sign up for a Grid Status account and copy your key from the [Settings page](https://www.gridstatus.io/settings/api).
2. **Provide the key.** Set `export GRIDSTATUS_API_KEY=your_api_key`, or pass it directly: `GridStatusClient(api_key="<your_api_key>")`.
3. **Find a dataset.** Call `client.list_datasets()` or browse the [Data Catalog](https://www.gridstatus.io/datasets).
4. **Query the dataset.** Call `client.get_dataset(...)` with the dataset ID and desired time range.

```python
from gridstatusio import GridStatusClient
client = GridStatusClient()

data = client.get_dataset('ercot_fuel_mix', limit=100, start='2025-01-01', end='2025-01-02')
client = GridStatusClient() # reads GRIDSTATUS_API_KEY from the environment

client.list_datasets()

df = client.get_dataset(
"ercot_fuel_mix",
start="2024-06-01",
end="2024-06-02",
limit=1000,
)
```

* To see all available datasets, use `client.list_datasets()` or check out the complete Grid Status catalog at https://www.gridstatus.io/datasets
### Dataset metadata

* To get metadata for a single dataset (description, available time range, columns, and more), use `client.get_dataset_metadata(dataset_id)`. It always returns a dictionary, with timestamp fields parsed into timezone-aware datetimes:
Use `client.get_dataset_metadata(dataset_id)` to get a dataset's description, available time range, columns, and more. It always returns a dictionary, with timestamp fields parsed into timezone-aware datetimes:

```python
metadata = client.get_dataset_metadata("ercot_fuel_mix")
Expand All @@ -63,21 +95,9 @@ metadata = client.get_dataset_metadata("ercot_fuel_mix")
# }
```

* **NOTE**: the Grid Status API has a 500,000 rows per month limit on the free plan. This limit is _very_ easy to exceed when querying data, especially real time prices.
* Make sure to add `limit` to all of your `get_dataset` calls to avoid quickly exceeding the limit.
## Return formats


* For more detailed examples, check out this notebook: [Getting Started](Examples/1.%20Getting%20Started.ipynb)
* Other notebooks in the [Examples](Examples) directory:
- [Finding Hubs and Zones in Pricing Data](Examples/2.%20ISO%20Hubs.ipynb)
- [ERCOT Pricing Data](Examples/3.%20ERCOT%20Pricing%20Data.ipynb)
- [CAISO April Net Load Analysis](Examples/4.%20CAISO%20April%20Net%20Load.ipynb)
- [Stacked Net Load Visualization](Examples/5.%20Stacked%20Net%20Load%20Visualization.ipynb)
- [Resample Data to Different Frequencies](Examples/6.%20Resampling%20Data.ipynb)

## Return Formats

The client supports three return formats for data: **pandas DataFrames**, **polars DataFrames**, and **Python objects** (list of dictionaries). You can specify the format at the client level or per-call.
`get_dataset(...)` supports pandas, polars, or Python objects. Set the return format at the client level or per-call. Dataset metadata is always returned as a dictionary.

```python
from gridstatusio import GridStatusClient
Expand All @@ -86,87 +106,57 @@ from gridstatusio import GridStatusClient
client = GridStatusClient(return_format="pandas") # or "polars" or "python"

# Override format for a specific call
data = client.get_dataset('ercot_fuel_mix', limit=100, return_format="python")
data = client.get_dataset("ercot_fuel_mix", limit=100, return_format="python")
```

### Format Options

| Format | Return Type | Description |
|--------|------------|-------------|
| `"pandas"` | `pd.DataFrame` | Pandas DataFrame with parsed datetime columns |
| `"polars"` | `pl.DataFrame` | Polars DataFrame with parsed datetime columns |
| `"python"` | `list[dict]` | List of dictionaries with parsed datetime columns |

### Default Behavior

If `return_format` is not specified, the client returns **pandas DataFrames** by default.

### Example: Python Format

```python
from gridstatusio import GridStatusClient

client = GridStatusClient(return_format="python")
data = client.get_dataset('ercot_fuel_mix', limit=5)

# Returns a list of dictionaries
# Python format → list of dicts:
# [
# {"interval_start_utc": "2025-01-01T00:00:00+00:00", "coal": 1234.5, ...},
# {"interval_start_utc": "2025-01-01T00:05:00+00:00", "coal": 1235.2, ...},
# ...
# ]
```

### Example: Polars Format
### Using without pandas (advanced)

```python
from gridstatusio import GridStatusClient

client = GridStatusClient(return_format="polars")
df = client.get_dataset('ercot_fuel_mix', limit=100)

# Returns a polars DataFrame
print(type(df)) # <class 'polars.dataframe.frame.DataFrame'>
```

### Using Without Pandas (Advanced)

While pandas is a required dependency, the library uses lazy loading so pandas is only imported when actually needed. This allows advanced users to use the library without pandas in minimal environments:
Pandas is installed by default, but the library uses lazy loading so it's only imported when needed. In minimal environments, you can install without dependencies and use `return_format="python"` to avoid pandas entirely:

```bash
# Install without dependencies (advanced usage only)
uv pip install gridstatusio --no-deps

# Then manually install only the required non-pandas dependencies
uv pip install requests termcolor tabulate
```

When using the library without pandas:

```python
from gridstatusio import GridStatusClient

# Must explicitly set return_format="python" to avoid pandas import
# Must explicitly set return_format="python" to avoid the pandas import
client = GridStatusClient(api_key="your_key", return_format="python")
data = client.get_dataset('ercot_fuel_mix', limit=100)

# Returns list of dicts - no pandas required
data = client.get_dataset("ercot_fuel_mix", limit=100)
```

Note: If you don't specify `return_format="python"`, the client will attempt to use pandas and raise an error if it's not installed.
If you don't set `return_format="python"`, the client attempts to use pandas and raises an error if it isn't installed.

## Checking your API usage

```python
usage = client.get_api_usage()
```

* This shows the limits for your API key, the start and end of the current usage period, and the API usage in the current period. Note a limit of -1 means no limit.
Shows the limits for your API key, the start/end of the current usage period, and usage in the current period. A limit of `-1` means no limit.

## Retry Configuration
The free plan allows 500,000 rows per month. You can view detailed usage stats in [Settings](https://www.gridstatus.io/settings/usage).

* The Grid Status API has rate limits that restrict the number of requests that are allowed each second, minute and hour. If rate limits are hit the client will automatically retry the request after a delay. You can configure the maximum number of retries using the `max_retries` parameter when initializing the client. If you find yourself hitting rate limits, you may need to add a delay between your requests. The [Grid Status Pricing Page](https://www.gridstatus.io/pricing) contains more details on specific rate limits.
* The client retries failed requests due to rate limits (429), server errors (5xx), and network issues using exponential backoff. You can customize retry behavior:
## Retry configuration

The API enforces per-second/minute/hour rate limits. The client retries rate limits (429), server errors (5xx), and network issues with exponential backoff. See the [Pricing Page](https://www.gridstatus.io/pricing) for specific limits.

```python
client = GridStatusClient(
Expand All @@ -176,28 +166,31 @@ client = GridStatusClient(
)
```

The retry delay follows the formula `delay = base_delay * (exponential_base ** retry_count)`.

Retries are useful when:
Set `max_retries=0` to disable retries.

* You're making pagination-heavy requests and risk hitting short-term rate limits
* A request fails due to a temporary server error
* A network issue or timeout interrupts the request
## Version check

To disable retries entirely, set `max_retries=0`.

## Version Check

The client checks for updates to the library when it is imported. It does this by making a call to the [library on PyPI](https://pypi.org/project/gridstatusio/). For certain applications, this call can be problematic, and can be disabled by setting the environment variable `GSIO_SKIP_VERSION_CHECK` to `true`.
The client checks PyPI for library updates on import. To disable this (e.g. in restricted environments), set:

```bash
export GSIO_SKIP_VERSION_CHECK=true
```

## Open Source
## More examples

- [Getting Started](Examples/1.%20Getting%20Started.ipynb)
- [Finding Hubs and Zones in Pricing Data](Examples/2.%20ISO%20Hubs.ipynb)
- [ERCOT Pricing Data](Examples/3.%20ERCOT%20Pricing%20Data.ipynb)
- [CAISO April Net Load Analysis](Examples/4.%20CAISO%20April%20Net%20Load.ipynb)
- [Stacked Net Load Visualization](Examples/5.%20Stacked%20Net%20Load%20Visualization.ipynb)
- [Resample Data to Different Frequencies](Examples/6.%20Resampling%20Data.ipynb)

## Resources

If you prefer to use an open source library that fetches data directly from the source, you can check out this [github repo](https://github.com/gridstatus/gridstatus).
- [OpenAPI spec](https://api.gridstatus.io/openapi.json)
- [LLMs manifest](https://gridstatus.io/llms.txt)
- [Docs assistant](https://docs.gridstatus.io/)

## Get Help
## Get help

We'd love to answer any usage or data access questions! Please let us know by emailing us at contact@gridstatus.io
For usage or data-access questions, email contact@gridstatus.io.
Binary file modified gridstatus-header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added gridstatusio/py.typed
Empty file.
37 changes: 34 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
[project]
name = "gridstatusio"
version = "0.16.0"
Comment thread
WillKoehrsen marked this conversation as resolved.
description = "Python Client for GridStatus.io API"
description = "Python client for the GridStatus.io API: normalized electricity market data (load, fuel mix, LMPs, forecasts) from every North American ISO through a single REST API."
authors = [{ name = "Max Kanter", email = "kmax12@gmail.com" }]
requires-python = ">=3.10, <4"
readme = "README.md"
license.file = "LICENSE"
maintainers = [{ name = "Max Kanter", email = "kmax12@gmail.com" }]
keywords = [
"energy",
"electricity",
"power grid",
"independent system operator",
"ISO",
"RTO",
"CAISO",
"ERCOT",
"PJM",
"MISO",
"SPP",
"NYISO",
"ISO-NE",
"IESO",
"AESO",
"EIA",
"LMP",
"electricity prices",
"energy markets",
"time series",
"grid data",
"API client",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Intended Audience :: Financial and Insurance Industry",
"Topic :: Scientific/Engineering :: Information Analysis",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
Expand Down Expand Up @@ -48,8 +75,12 @@ all = [
]

[project.urls]
"Source Code" = "https://github.com/kmax12/gridstatusio/"
"Issue Tracker" = "https://github.com/kmax12/gridstatusio/issues"
"Homepage" = "https://www.gridstatus.io/"
"Documentation" = "https://www.gridstatus.io/api"
"Data Catalog" = "https://www.gridstatus.io/datasets"
"Source Code" = "https://github.com/gridstatus/gridstatusio/"
"Changes" = "https://github.com/gridstatus/gridstatusio/blob/main/CHANGELOG.md"
"Issue Tracker" = "https://github.com/gridstatus/gridstatusio/issues"

[tool.uv]
dev-dependencies = [
Expand Down
Loading