Liveness probe. Public — no Authorization header required. Use for uptime monitoring.
No parameters.
curl https://loadconsensus.com/api/v1/health{
"ok": true,
"timestamp": "2026-05-15T12:00:00.000Z",
"version": "v1.0.0"
}Fetching
Public read API
Programmatic access to US power forecasts and datacenter pipeline. JSON over HTTPS, bearer-token auth, predictable per-key daily quotas.
Official clients are first-class — they use the same bearer auth, observe the same per-key daily rate limits, and return the same response shapes as the raw REST API. Versioning tracks the API version (v1).
npm install @loadconsensus/sdkimport { LoadConsensusClient } from '@loadconsensus/sdk';
const client = new LoadConsensusClient({ apiKey: process.env.LC_API_KEY! });
const { data } = await client.consensus({
isoRegion: 'ERCOT',
metric: 'peak-demand-gw',
targetYear: 2030,
});
console.log(data.median, data.unit, `(${data.sourceCount} sources)`);
// client.rateLimit -> { limit, remaining, reset }pip install loadconsensusfrom loadconsensus import Client
client = Client(api_key="lc_...")
result = client.consensus(iso_region="ERCOT", metric="peak-demand-gw", target_year=2030)
data = result["data"]
print(data["median"], data["unit"], f"({data['sourceCount']} sources)")
# client.rate_limit -> RateLimit(limit=..., remaining=..., reset=...)Source for both clients lives in the monorepo at github.com/001Sir/loadconsensus/tree/main/sdks.
Every endpoint except /health requires an Authorization: Bearer lc_<key> header. Keys are issued at Pro tier and above — see pricing to subscribe. Keys are shown once at creation; we store only a sha-256 hash.
curl -H "Authorization: Bearer lc_..." https://loadconsensus.com/api/v1/sourcesIn local development with NODE_ENV !== "production", requests without the header fall through to a dev bypass capped at 100 calls/day. This bypass is never active in production.
| Tier | Daily quota |
|---|---|
| Pro | 1,000 / day |
| Team | 50,000 / day |
| Enterprise | Custom |
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (UTC seconds). Quotas reset at UTC midnight.
Liveness probe. Public — no Authorization header required. Use for uptime monitoring.
No parameters.
curl https://loadconsensus.com/api/v1/health{
"ok": true,
"timestamp": "2026-05-15T12:00:00.000Z",
"version": "v1.0.0"
}List active forecast publishers. Filter by category (federal | rto | state-puc | commercial) or isoRegion.
| Param | Type | Description |
|---|---|---|
| category | string | federal | rto | state-puc | commercial |
| isoRegion | string | ERCOT | PJM | MISO | CAISO | ISO-NE | NYISO | SPP | NATIONAL |
curl -H "Authorization: Bearer lc_..." \
"https://loadconsensus.com/api/v1/sources?category=federal"{
"data": [
{
"id": "eia-aeo",
"name": "EIA Annual Energy Outlook",
"category": "federal",
"isoRegion": "NATIONAL",
"baseUrl": "https://www.eia.gov/outlooks/aeo/",
"releaseSchedule": "Annual, April"
},
{
"id": "eia-steo",
"name": "EIA Short-Term Energy Outlook",
"category": "federal",
"isoRegion": "NATIONAL",
"baseUrl": "https://www.eia.gov/outlooks/steo/",
"releaseSchedule": "Monthly, 2nd Tuesday"
}
],
"meta": { "count": 2 }
}List the latest forecast rows. Each row is one (source × ISO × metric × year) tuple. Default page size 100, max 500.
| Param | Type | Description |
|---|---|---|
| isoRegion | string | ERCOT | PJM | MISO | CAISO | ISO-NE | NYISO | SPP | NATIONAL |
| metric | string | peak-demand-gw | annual-energy-twh | annual-generation-twh | net-load-gw | load-growth-pct |
| targetYear | integer | Forecast horizon year (e.g. 2026, 2030) |
| sourceId | string | Restrict to a single publisher (e.g. eia-steo) |
| limit | integer | Page size. Default 100, max 500. |
curl -H "Authorization: Bearer lc_..." \
"https://loadconsensus.com/api/v1/load-forecasts?isoRegion=CAISO&metric=annual-energy-twh&targetYear=2026"{
"data": [
{
"id": "lf_13e2334bf2634bb9e8c6fdb0",
"sourceId": "eia-steo",
"documentId": null,
"isoRegion": "CAISO",
"metric": "annual-energy-twh",
"targetYear": 2026,
"targetSeason": null,
"value": 253.59537472,
"unit": "TWh",
"scenarioLabel": "reference",
"notes": null,
"fetchedAt": "2026-05-14T00:00:00.000Z"
},
{
"id": "lf_nerc_caiso_energy_2026",
"sourceId": "nerc-ltra",
"documentId": null,
"isoRegion": "CAISO",
"metric": "annual-energy-twh",
"targetYear": 2026,
"targetSeason": null,
"value": 266,
"unit": "TWh",
"scenarioLabel": "reference",
"notes": null,
"fetchedAt": "2026-05-14T00:00:00.000Z"
}
],
"meta": { "count": 2, "sources": ["eia-steo", "nerc-ltra"] }
}Cross-source consensus (min / median / max) for a single (isoRegion, metric, targetYear) tuple. Returns 404 when no rows match.
| Param | Type | Description |
|---|---|---|
| isoRegion* | string | e.g. CAISO |
| metric* | string | e.g. annual-energy-twh |
| targetYear* | integer | e.g. 2026 |
curl -H "Authorization: Bearer lc_..." \
"https://loadconsensus.com/api/v1/consensus?isoRegion=CAISO&metric=annual-energy-twh&targetYear=2026"{
"data": {
"isoRegion": "CAISO",
"metric": "annual-energy-twh",
"targetYear": 2026,
"unit": "TWh",
"min": 253.59537472,
"max": 266,
"median": 259.79768736,
"sourceCount": 2,
"values": [
{ "sourceId": "eia-steo", "value": 253.59537472 },
{ "sourceId": "nerc-ltra", "value": 266 }
]
}
}List tracked datacenter projects, sorted by the larger of operatingMw / announcedMw. Filter by ISO, state, status, or operator.
| Param | Type | Description |
|---|---|---|
| isoRegion | string | ERCOT | PJM | MISO | ... |
| state | string | Two-letter US state code (e.g. VA, TX) |
| status | string | announced | permitted | under-construction | operating | cancelled |
| operator | string | e.g. Amazon, Meta, Microsoft |
curl -H "Authorization: Bearer lc_..." \
"https://loadconsensus.com/api/v1/datacenter-projects?status=operating"{
"data": [
{
"id": "dcp_amazon_ashburn",
"slug": "amazon-ashburn-va",
"name": "AWS US-East-1 Ashburn Campus",
"operator": "Amazon",
"utilityId": null,
"isoRegion": "PJM",
"state": "VA",
"county": "Loudoun",
"city": "Ashburn",
"lat": 39.0438,
"lng": -77.4874,
"announcedMw": null,
"operatingMw": 2500,
"status": "operating",
"announcedDate": null,
"operationalDate": "2020-01-01",
"sourceUrl": null,
"sourceNotes": null,
"sources": [],
"createdAt": "2026-05-14T00:00:00.000Z",
"updatedAt": "2026-05-14T00:00:00.000Z"
}
],
"meta": { "count": 1, "totalAnnouncedMw": 0, "totalOperatingMw": 2500 }
}Fetch a single datacenter project by slug. 404 when no match.
| Param | Type | Description |
|---|---|---|
| slug* | string | Path segment, e.g. amazon-ashburn-va |
curl -H "Authorization: Bearer lc_..." \
"https://loadconsensus.com/api/v1/datacenter-projects/meta-richland-parish-la"{
"data": {
"id": "dcp_meta_richland",
"slug": "meta-richland-parish-la",
"name": "Meta Richland Parish Hyperion",
"operator": "Meta",
"utilityId": null,
"isoRegion": "MISO",
"state": "LA",
"county": "Richland Parish",
"city": null,
"lat": null,
"lng": null,
"announcedMw": 2000,
"operatingMw": null,
"status": "announced",
"announcedDate": "2024-12-04",
"operationalDate": null,
"sourceUrl": null,
"sourceNotes": null,
"sources": [],
"createdAt": "2026-05-14T00:00:00.000Z",
"updatedAt": "2026-05-14T00:00:00.000Z"
}
}Upcoming forecast-release calendar events. Defaults to a 90-day window from today. Filter by ISO.
| Param | Type | Description |
|---|---|---|
| isoRegion | string | e.g. PJM |
| from | ISO date | Inclusive lower bound. Defaults to today (UTC). |
| to | ISO date | Inclusive upper bound. Defaults to 90 days after `from`. |
curl -H "Authorization: Bearer lc_..." \
"https://loadconsensus.com/api/v1/power-events?isoRegion=PJM"{
"data": [
{
"id": "pe_jan_pjm_load_2026",
"source": "pjm-load-forecast",
"externalId": null,
"eventName": "PJM Load Forecast Report 2026",
"isoRegion": "PJM",
"category": "forecast-release",
"date": "2026-01-30T16:00:00.000Z",
"description": null,
"url": "https://www.pjm.com/library/reports-notices/load-forecast-report.aspx"
}
],
"meta": {
"count": 1,
"from": "2026-05-15T00:00:00.000Z",
"to": "2026-08-13T00:00:00.000Z"
}
}Pro+ tier. Each CSV route mirrors the query parameters of its JSON sibling and adds standard download headers (text/csv; charset=utf-8, Content-Disposition: attachment). A Authorization: Bearer lc_... header is required — Free-tier keys and the local dev bypass receive a 402 upgrade_required response so the tier gate is validated end-to-end.
Per-tier row caps: Pro 50,000, Team 200,000, Enterprise 1,000,000. Files are returned in a single response — no pagination cursors.
curl -H "Authorization: Bearer lc_abc..." \
"https://loadconsensus.com/api/v1/load-forecasts.csv?isoRegion=ERCOT&metric=annual-energy-twh" \
-o ercot-forecasts.csvLatest forecast rows as CSV. Same filters as the JSON sibling. Per-tier row caps: Pro 50K, Team 200K, Enterprise 1M.
load-forecasts-YYYYMMDD.csvid, sourceId, isoRegion, metric, targetYear, targetSeason, value, unit, scenarioLabel, isLatest, fetchedAt| Param | Type | Description |
|---|---|---|
| isoRegion | string | ERCOT | PJM | MISO | CAISO | ISO-NE | NYISO | SPP | NATIONAL |
| metric | string | peak-demand-gw | annual-energy-twh | ... |
| targetYear | integer | Forecast horizon year. |
| sourceId | string | Restrict to a single publisher. |
Datacenter pipeline as CSV. Excludes the JSONB `sources` array and long-text `sourceNotes` by default; add `?include=sources,notes` to opt in (rendered as JSON-stringified cells).
datacenter-projects-YYYYMMDD.csvid, slug, name, operator, utilityId, isoRegion, state, city, lat, lng, announcedMw, operatingMw, status, announcedDate, sourceUrl| Param | Type | Description |
|---|---|---|
| isoRegion | string | ERCOT | PJM | MISO | ... |
| state | string | Two-letter US state code. |
| status | string | announced | permitted | under-construction | operating | cancelled |
| operator | string | e.g. Amazon, Meta, Microsoft |
| include | string | Comma list: `sources`, `notes`. |
Forecast revision history. Filter by `since` (ISO date) and `minAbsPct` (only rows where |deltaPct| ≥ N). Same Pro/Team/Enterprise row caps as load-forecasts.csv.
forecast-revisions-YYYYMMDD.csvid, forecastId, sourceId, isoRegion, metric, targetYear, priorValue, deltaAbsolute, deltaPct, recordedAt| Param | Type | Description |
|---|---|---|
| since | ISO date | Inclusive lower bound on recordedAt. |
| minAbsPct | number | Drop rows where |deltaPct| < this value. |
Pro+ tier. Configure an https URL at /account/webhooks and receive signed HTTP POSTs when matching events occur. Each subscription has a dedicated signing secret; you verify payloads by recomputing the HMAC-SHA256 over the raw request body.
| Type | Fires when |
|---|---|
| forecast.revision | A LoadForecast row was updated — payload includes priorValue, newValue, deltaAbsolute, deltaPct, isoRegion, metric, targetYear. |
| dc.announcement | A new DatacenterProject row was created, or a tracked field changed (operator, status, announcedMw, operatingMw). |
| jobrun.failure | A cron worker recorded status='failed' in JobRun. Payload includes jobName, fail count, errorSample. |
Each subscription accepts an isoFilters allowlist (empty = all regions) and an optional minDeltaPct floor — forecast.revision events with abs(deltaPct) < minDeltaPct are skipped.
{
"id": "whd_<hex>",
"type": "forecast.revision",
"createdAt": "2026-05-15T12:00:00.000Z",
"data": {
"revisionId": "...",
"forecastId": "lf_...",
"sourceId": "eia-steo",
"isoRegion": "ERCOT",
"metric": "annual-energy-twh",
"targetYear": 2026,
"priorValue": 482.1,
"newValue": 491.7,
"unit": "TWh",
"deltaAbsolute": 9.6,
"deltaPct": 1.99,
"recordedAt": "2026-05-15T11:59:42.000Z"
}
}| Header | Value |
|---|---|
| X-LoadConsensus-Signature | sha256=<hex of HMAC-SHA256(secret, raw_body)> |
| X-LoadConsensus-Event | e.g. forecast.revision |
| X-LoadConsensus-Delivery | whd_<hex> — unique per attempt |
Compute the same HMAC over the raw request body and constant-time compare against the header value (strip the sha256= prefix). Reject on mismatch before parsing JSON.
# Receiver side — verify and respond.
# RAW_BODY must be the exact bytes from the request (do not re-serialize).
SECRET="whsec_..."
SIG_HEADER="$HTTP_X_LOADCONSENSUS_SIGNATURE" # e.g. "sha256=abc123..."
RAW_BODY="$(cat)"
expected="sha256=$(printf '%s' "$RAW_BODY" \
| openssl dgst -sha256 -hmac "$SECRET" \
| awk '{print $2}')"
if [ "$expected" = "$SIG_HEADER" ]; then
echo "OK"; exit 0
else
echo "signature mismatch"; exit 1
fiimport { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook(rawBody: string, header: string, secret: string): boolean {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(header);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}All error responses use the shape { error, hint? }. The HTTP status is the source of truth.
{ "error": "unauthorized", "hint": "Include Authorization: Bearer lc_... header" }{ "error": "upgrade_required", "hint": "CSV export requires Pro or higher" }{ "error": "not_found", "hint": "No forecasts for ERCOT / peak-demand-gw / 2099" }{ "error": "rate_limit_exceeded", "remaining": 0, "reset": 1747353600 }{ "error": "server_error" }