{
  "markdown": "# Clarigrid\n\nUnified Python SDK for European and U.S. energy market data.\n\n[![PyPI](https://img.shields.io/pypi/v/clarigrid)](https://pypi.org/project/clarigrid/)\n[![Python](https://img.shields.io/pypi/pyversions/clarigrid)](https://pypi.org/project/clarigrid/)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n\n---\n\n## What it is\n\nClarigrid provides a single, stable Python interface to access and normalise\nEuropean and U.S. energy market data from multiple sources. All data comes back as\ntimezone-aware pandas DataFrames with consistent column names and units.\n\nBuilt-in free providers (no API key required) include **Energy-Charts**\n(Europe), **Energinet** (DK1/DK2), **SMARD** (DE), **Elia** (BE), **NESO**\n(GB), **Elexon/BMRS** (GB), and **ENTSOG** (EU gas).\nFingrid (FI), GIE AGSI/ALSI (European gas), and TenneT (NL) are also built in\nand use free API keys.\nFor the United States, CAISO OASIS and NYISO provide no-auth market and system\ndata. EIA-930 provides nationwide hourly balancing-authority load, forecasts,\nfuel generation, and physical interchange with a free EIA key.\nGlobal historical meteorology and solar data are available from NASA POWER\nwithout an API key.\nENTSO-E and other key-protected sources can be configured as described in\n[API key setup](#api-key-setup) below.\n\n---\n\n## Install\n\n```bash\npip install clarigrid\n```\n\nFor the interactive setup wizard and CLI tools:\n\n```bash\npip install clarigrid[auth]\n```\n\n---\n\n## Quick start\n\n```python\nimport clarigrid as cg\n\n# Free providers — no key required.\ncg.connect(\"smard\")   # DE prices, load, generation\ncg.connect(\"elia\")    # BE load, generation\ncg.connect(\"neso\")    # GB load, embedded generation\ncg.connect(\"elexon\")  # GB prices, generation mix\ncg.connect(\"entsog\")  # EU gas flows (any TSO zone)\ncg.connect(\"energycharts\")  # European prices, power, forecasts and flows\ncg.connect(\"energinet\")  # DK1/DK2 prices, power, forecasts, flows and CO2\ncg.connect(\"redata\")  # ES load, generation, capacity and cross-border flows\ncg.connect(\"rte\")  # FR load, generation, forecasts, exchanges and CO2\ncg.connect(\"fingrid\")  # FI power, forecasts, flows, balancing and CO2 (free key)\ncg.connect(\"gie\")  # European gas storage and LNG inventory (free key)\ncg.connect(\"eia\")  # US balancing-authority load, generation and flows (free key)\ncg.connect(\"caiso\")  # CAISO day-ahead hub prices (no key)\ncg.connect(\"nyiso\")  # NYISO prices, load, forecasts and fuel mix (no key)\ncg.connect(\"nasapower\")  # Global daily/hourly weather and solar data (no key)\n\n# Optional: set output timezone (default is UTC).\ncg.set_timezone(\"Europe/Brussels\")\n\n# Fetch data — provider is chosen automatically by zone.\nprices = cg.get_prices(\"DE\", \"2025-01-01\", \"2025-01-07\")  # → smard\nload   = cg.get_load(\"BE\",   \"2025-01-01\", \"2025-01-07\")  # → elia\ngen    = cg.get_generation(\"GB\", \"2025-01-01\", \"2025-01-07\")  # → elexon\ngas    = cg.get_gas_flows(\"BE-TSO-0001\", \"2025-01-01\", \"2025-01-07\")  # → entsog\nus_load = cg.get_load(\"CAISO\", \"2025-01-01\", \"2025-01-07\")  # → eia (CISO)\nnp15 = cg.get_prices(\"CISO_NP15\", \"2025-01-01\", \"2025-01-07\")  # → caiso\nnyc = cg.get_prices(\"NYISO_NYC\", \"2025-01-01\", \"2025-01-07\")  # → nyiso\nweather = cg.get_weather(\n    \"40.7128,-74.0060\",\n    \"2025-01-01\",\n    \"2025-01-07\",\n    source=\"nasapower\",\n)\n```\n\n---\n\n## API key setup\n\nSome providers (ENTSO-E, TenneT) require a personal API key issued by\nthe upstream data source.  Clarigrid supports two ways to supply these keys.\n\n### Option 1 — ClarigGrid account (recommended)\n\nStore all your provider keys in one place at\n[clarigrid.energy/saved](https://clarigrid.energy/saved).  The SDK then\nfetches them automatically using a single **ClarigGrid API key**.\n\n**First-time setup (interactive):**\n\n```python\nimport clarigrid as cg\ncg.connect(\"entsoe\")\n# Opens browser → log in at clarigrid.energy → keys fetched automatically.\n```\n\nOr use the CLI:\n\n```bash\nclarigrid setup          # guided wizard for all providers\nclarigrid connect entsoe # authenticate a single provider\n```\n\n**Headless / CI environments:**  set one environment variable and no\nbrowser is ever needed:\n\n```bash\nexport CLARIGRID_API_KEY=your-clarigrid-uuid\n```\n\nThe SDK uses `CLARIGRID_API_KEY` to fetch all your stored provider keys\nfrom clarigrid.energy on the first `connect()` call of each session.\n\n**How to get a `CLARIGRID_API_KEY`:**\n\n1. Log in at [clarigrid.energy](https://clarigrid.energy)\n2. Add your provider API keys at [clarigrid.energy/saved](https://clarigrid.energy/saved)\n3. Run `cg.connect(\"entsoe\")` once in an interactive terminal — the browser\n   flow logs you in and stores your `CLARIGRID_API_KEY` locally.\n\n---\n\n### Option 2 — Manual key entry (no account needed)\n\nIf you prefer not to use a clarigrid.energy account, set provider keys\ndirectly. Keys are stored in `~/.config/clarigrid/.env` (permissions: 600).\n\n**Environment variable** (recommended for CI):\n\n```bash\nexport ENTSOE_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\nexport TENNET_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n```\n\n**Config file** — add to `~/.config/clarigrid/.env`:\n\n```\nENTSOE_API_KEY=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\nTENNET_API_KEY=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n```\n\n## Zone routing\n\nEach call to `cg.connect()` registers a provider and its capability-specific\nzone coverage in\nan internal router.  When you call `get_prices(\"DE\")`, the router picks the\nbest connected provider for that zone and dataset automatically.\n\nMultiple `connect()` calls accumulate coverage.  If two providers both cover\nthe same zone/dataset pair, the **later** `connect()` call wins.\n\n```python\ncg.connect(\"neso\")    # covers GB: load, generation\ncg.connect(\"elexon\")  # covers GB: prices, generation — overwrites generation slot\n\n# Now: GB prices → elexon, GB load → neso, GB generation → elexon\nprices = cg.get_prices(\"GB\", \"2025-01-01\", \"2025-01-02\")\nload   = cg.get_load(\"GB\",   \"2025-01-01\", \"2025-01-02\")\n```\n\nIf no connected provider covers the requested zone/dataset, a helpful error\nis raised:\n\n```\nZoneNotCoveredError: No connected provider has 'prices' data for zone 'BE'.\n  Consider: cg.connect('entsoe')\n```\n\nTo bypass routing and force a specific provider:\n\n```python\ndf = cg.get_load(\"GB\", \"2025-01-01\", \"2025-01-07\", source=\"neso\")\n```\n\n---\n\n## Output format\n\nAll functions return a `pandas.DataFrame` with:\n\n| Property | Value |\n|---|---|\n| Index | `DatetimeIndex` named `utc_time`, tz-aware |\n| Timezone | UTC by default; change with `cg.set_timezone()` |\n| Price column | `price_mwh` |\n| Load column | `load_mw` |\n| Generation columns | fuel-type specific, e.g. `solar_mw`, `wind_onshore_mw`, `nuclear_mw` |\n| Gas flow column | `flow_kwh_d` |\n| Gas storage | inventory in `*_mwh`; daily rates in `*_mwh_d` |\n| LNG inventory | `*_thousand_m3`; send-out in `*_mwh_d` |\n| Cross-border columns | signed MW; imports positive, exports negative |\n| Installed capacity | `*_capacity_mw`; storage energy uses `*_energy_mwh` |\n| Frequency | `frequency_hz` |\n| Renewable shares | `*_pct` |\n\nPrice currency is stored in `df.attrs[\"currency\"]` (for example ``\"EUR\"``,\n``\"GBP\"``, or ``\"USD\"``):\n\n```python\ndf = cg.get_prices(\"DE\", \"2025-01-01\", \"2025-01-07\")\nprint(df.attrs[\"currency\"])  # 'EUR'\n```\n\nEuropean zone codes follow the ENTSO-E bidding zone convention (`BE`, `DE_LU`,\n`FR` ...). U.S. electricity uses EIA/NERC balancing-authority codes (`CISO`,\n`ERCO`, `PJM`, `NYIS`) and explicit market hubs (`CISO_NP15`). Common aliases\n(`DE` → `DE_LU`, `CAISO` → `CISO`, `ERCOT` → `ERCO`) resolve automatically.\n\n---\n\n## Timezone\n\n```python\ncg.set_timezone(\"Europe/Brussels\")   # all subsequent calls return Brussels time\ncg.set_timezone(\"UTC\")               # revert to default\n\ndf = cg.get_load(\"BE\", \"2025-01-01\", \"2025-01-07\")\n# df.index is tz-aware in Europe/Brussels\n```\n\nData is always fetched and cached as UTC.  Timezone conversion is applied\nat the output boundary only.\n\n---\n\n## Caching\n\nResponses are cached locally at `~/.clarigrid/cache/` as Parquet files\n(requires `pip install clarigrid[cache]`), keyed by provider + dataset +\nzone + date range.  Historical data is cached indefinitely; live data\nexpires after 1 hour by default.\n\n```python\nfrom clarigrid.core import cache\n\ncache.info()           # DataFrame showing cached entries\ncache.clear()          # clear all\ncache.clear(\"smard\")   # clear one provider\ncache.set_live_ttl(1800)  # change live-data TTL to 30 min\n```\n\nDisable caching per call:\n\n```python\ndf = cg.get_prices(\"DE\", \"2025-01-01\", \"2025-01-07\", use_cache=False)\n```\n\n---\n\n## CLI reference\n\nRequires `pip install clarigrid[auth]`.\n\n```bash\nclarigrid setup                    # guided wizard — configure all providers\nclarigrid connect <source>         # authenticate a single provider\nclarigrid auth --show              # list configured sources (keys masked)\nclarigrid auth --clear <source>    # remove key for a specific source\nclarigrid auth --clear --all       # remove all stored keys\n```\n\n---\n\n## API reference\n\n| Function | Description |\n|---|---|\n| `cg.connect(provider)` | Connect provider; handles auth for key-guarded sources |\n| `cg.set_timezone(tz)` | Set output timezone (IANA string, default `\"UTC\"`) |\n| `cg.get_prices(zone, start, end, market=\"day_ahead\", node=None)` | Electricity prices → `price_mwh`; optional node for nodal markets |\n| `cg.get_load(zone, start, end)` | Actual total load → `load_mw` |\n| `cg.get_generation(zone, start, end)` | Generation per fuel type → `*_mw` columns |\n| `cg.get_generation_forecast(zone, start, end)` | Wind/solar generation forecast → `*_forecast_mw` |\n| `cg.get_load_forecast(zone, start, end)` | Load forecast → `load_forecast_mw` |\n| `cg.get_physical_flows(zone, start, end)` | Signed cross-border physical flows in MW |\n| `cg.get_commercial_schedule(zone, start, end)` | Signed commercial exchanges in MW |\n| `cg.get_installed_capacity(zone, start, end)` | Installed power by technology in MW |\n| `cg.get_frequency(zone, start, end)` | System frequency → `frequency_hz` |\n| `cg.get_renewable_share(zone, start, end)` | Renewable share in percent |\n| `cg.get_co2_intensity(zone, start, end)` | Electricity carbon intensity in gCO2/kWh |\n| `cg.get_co2_forecast(zone, start, end)` | Forecast carbon intensity in gCO2/kWh |\n| `cg.get_gas_flows(zone, start, end)` | Gas physical flows → `flow_kwh_d` |\n| `cg.get_capacity(zone, start, end)` | Firm technical gas capacity → `capacity_kwh_d` |\n| `cg.get_gas_storage(zone, start, end)` | Gas inventory, capacity, injection and withdrawal |\n| `cg.get_lng_inventory(zone, start, end)` | LNG tank inventory and terminal send-out |\n| `cg.get_weather(zone, start, end)` | Weather observations / forecasts |\n| `cg.status()` | Print connected providers, zones, capabilities |\n| `cg.set_api_key(provider, key)` | Store a provider key locally |\n| `cg.list_providers()` | List all registered provider names |\n| `cg.register_provider(name, instance)` | Register an external provider |\n\nAll data functions accept:\n- `source=\"name\"` — override the router for this call only\n- `use_cache=False` — bypass the local cache\n\n---\n\n## Built-in providers\n\n| Name | Data | Zones | Auth |\n|---|---|---|---|\n| `energycharts` | prices, load, generation, forecasts, capacity, cross-border flows, frequency, renewable share | European countries and openly licensed price zones | None |\n| `energinet` | prices, load, generation, forecasts, physical flows, actual/forecast CO2 | DK1, DK2 | None |\n| `redata` | five-minute load/forecast; daily-average generation, shares and physical flows; installed capacity | ES | None |\n| `rte` | load, generation, load forecasts, physical/commercial exchanges, generation shares and CO2 | FR | None |\n| `fingrid` | load, generation, forecasts, flows, NTC, capacity, frequency, CO2, imbalance and balancing | FI | Free API key |\n| `gie` | daily underground gas storage and LNG terminal inventory | Europe, countries, facilities | Free API key |\n| `eia` | hourly load, forecast, fuel generation, physical interchange and generation shares | U.S. balancing authorities and regions | Free API key |\n| `caiso` | day-ahead LMP at NP15, SP15, ZP26 or an explicit node | California ISO | None |\n| `nyiso` | day-ahead zonal LBMP, actual/forecast load, fuel mix and shares | New York ISO and NYISO load zones | None |\n| `nasapower` | daily/hourly meteorology, precipitation, wind and solar radiation | Global point locations (`lat,lon`) | None |\n| `smard` | prices, load, generation | DE, AT, LU + TSO sub-zones | None |\n| `elia` | load, generation | BE | None |\n| `neso` | load, embedded generation, actual/forecast CO2, generation shares | GB | None |\n| `elexon` | prices, generation mix | GB | None |\n| `entsog` | gas flows, capacity | All ENTSOG operators | None |\n| `entsoe` | prices, load, generation | All ENTSO-E bidding zones | `ENTSOE_API_KEY` |\n| `tennet` | *(data fetching coming soon)* | NL | `TENNET_API_KEY` |\n\nKeys for `entsoe` and `tennet` are issued by the respective upstream provider.\nStore them via a [clarigrid.energy](https://clarigrid.energy) account or\nset them manually as described in [API key setup](#api-key-setup).\n\n---\n\n## Architecture\n\n```\nclarigrid/\n├── __init__.py           # public surface: connect, get_prices, set_timezone, …\n├── _auth.py              # KeyState machine, auth flows, provider key registry\n├── _keystore.py          # ~/.config/clarigrid/.env  read/write (chmod 600)\n├── _browser_flow.py      # browser-based OAuth flow (localhost callback server)\n├── cli.py                # clarigrid CLI (setup, connect, auth)\n├── core/\n│   ├── api.py            # top-level functions — routing + normalisation\n│   ├── router.py         # ZoneRouter — (zone, capability) → provider\n│   ├── session.py        # runtime state: router, connected map, output TZ\n│   ├── normalise.py      # canonical column names + unit normalisation\n│   ├── registry.py       # register_provider / get_provider\n│   ├── interface.py      # DataProvider ABC ← providers implement this\n│   ├── cache.py          # filesystem Parquet cache\n│   ├── config.py         # legacy key store (~/.clarigrid/keys.toml)\n│   ├── exceptions.py     # exception hierarchy\n│   └── types.py          # shared constants, zone aliases\n├── providers/\n│   ├── smard.py          # Bundesnetzagentur SMARD (DE)\n│   ├── energycharts.py   # Fraunhofer ISE Energy-Charts (Europe)\n│   ├── energinet.py      # Energinet Energi Data Service (DK1/DK2)\n│   ├── redata.py         # Red Electrica REData (ES)\n│   ├── rte.py            # RTE Eco2mix (FR)\n│   ├── elia.py           # Elia Open Data (BE)\n│   ├── neso.py           # NESO Data Portal (GB)\n│   ├── elexon.py         # Elexon BMRS (GB)\n│   ├── eia.py            # EIA-930 balancing-authority operations (US)\n│   ├── caiso.py          # CAISO OASIS day-ahead prices (US)\n│   ├── nyiso.py          # NYISO prices, load, forecasts and fuel mix (US)\n│   ├── nasapower.py      # NASA POWER meteorology and solar data (global)\n│   └── entsog.py         # ENTSOG Transparency Platform (EU gas)\n└── utils/\n    ├── time.py           # parse_dt, normalise_index\n    └── validation.py     # resolve_zone, validate_date_range\n```\n\n### Plugin system\n\nExternal providers subclass `DataProvider`, declare their `zones()` and\n`capabilities()`, and self-register on import. Providers whose capabilities\nhave different geographical coverage can additionally override\n`capability_zones()`:\n\n```python\nfrom clarigrid.core.interface import DataProvider\nfrom clarigrid.core.registry import register_provider\nimport pandas as pd\n\nclass NordpoolProvider(DataProvider):\n    def zones(self) -> set[str]:\n        return {\"NO1\", \"NO2\", \"SE1\", \"SE2\", \"DK1\", \"DK2\", \"FI\"}\n\n    def capabilities(self) -> set[str]:\n        return {\"prices\"}\n\n    def get_prices(self, zone, start, end, **kwargs) -> pd.DataFrame: ...\n\nregister_provider(\"nordpool\", NordpoolProvider())\n```\n\nAfter `cg.connect(\"nordpool\")`, calls to `cg.get_prices(\"NO1\", …)` route\nto this provider automatically.\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/clarigrid/clarigrid\ncd clarigrid\npip install -e \".[dev]\"\npytest\nruff check .\nmypy clarigrid\n```\n\n---\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n\nCopyright (c) 2026 Alexander Hoogsteyn.\n",
  "bytes": 16222,
  "sha": "f998b47bc8228f2072327137e25af56673b231ee61676b9f7b42114b4a60cc9f",
  "repo_slug": "alexanderhoogsteyn/clarigrid",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_energy_clarigrid_clarigrid_db08326e/readme"
}