Developer Docs

REST API and MCP server.

Query the 885-plant US solar distress dataset programmatically. All data is public, all endpoints are free, no API key required. Built for analysts who live in Excel and AI assistants that need structured data.

PUBLIC · NO AUTH · CSV + JSON
REST API

Query over HTTP.

All endpoints return JSON by default. Add ?format=csv to any endpoint for an Excel-ready download. No authentication required.

Base URLhttps://solar-distress-map.vercel.app
GET/api/plants

List and filter all 885 plants. Results are sorted by distress score descending. Supports pagination.

ParameterTypeDescription
statestringUS state code, e.g. TX, CA, FL
nerc_regionstringMRO, NPCC, RFC, SERC, TRE, WECC
min_gapnumberUpper bound on gap (negative = underperforming). e.g. -0.15 returns plants worse than -15%
min_scorenumberMinimum distress score (0-100)
min_mwnumberMinimum capacity in MW AC
distress_flavorstringhail-impaired, curtailment-suspect, chronic-underperformer, vintage-risk, mild-underperformer, no-signal
ownerstringCase-insensitive partial match on owner/operator name
curtailment_suspectbooleantrue or false
residual_flagbooleantrue returns plants with unexplained gap (gap <= -10%, not curtailment)
limitnumberMax results (default 50, max 500)
offsetnumberPagination offset (default 0)
formatstringjson (default) or csv
Example
curl "https://solar-distress-map.vercel.app/api/plants?nerc_region=SERC&min_gap=-0.15&limit=5"

# CSV for Excel:
curl "https://solar-distress-map.vercel.app/api/plants?nerc_region=SERC&min_gap=-0.15&format=csv" -o serc_screen.csv
Response
{
  "plants": [
    {
      "plant_id": 59513,
      "plant_name": "Meadows PV 1",
      "owner": "Fresh Air Energy XVIII, LLC",
      "state": "NC",
      "nerc_region": "SERC",
      "capacity_ac_mw": 20,
      "cod_year": 2016,
      "gap_pct_12mo": -0.843,
      "distress_score": 87,
      "distress_flavor": "chronic-underperformer",
      "curtailment_suspect": false
    },
    ...
  ],
  "total": 53,
  "offset": 0,
  "limit": 5
}
GET/api/plants/:id

Full record for a single plant. Returns enriched data (flags, gap decomposition, narrative) for top-20 distress plants.

ParameterTypeDescription
formatstringjson (default) or csv
Example
curl "https://solar-distress-map.vercel.app/api/plants/59513"
Response
{
  "plant": {
    "plant_id": 59513,
    "plant_name": "Meadows PV 1",
    "gap_pct_12mo": -0.843,
    "distress_score": 87,
    "flags": ["RESIDUAL", "GAP"],
    "gap_decomposition": {
      "curtailment": 0,
      "hail": -0.02,
      "vintage": -0.04,
      "hybrid": 0,
      "residual": -0.783
    },
    "narrative": "...",
    ...
  },
  "source": "top20"
}
GET/api/plants/:id/performance

Monthly capacity factor vs peer median for a plant. Shows gap_pct per month.

ParameterTypeDescription
formatstringjson (default) or csv
Example
curl "https://solar-distress-map.vercel.app/api/plants/59513/performance"

# CSV time series for charting in Excel:
curl "https://solar-distress-map.vercel.app/api/plants/59513/performance?format=csv" -o performance.csv
Response
{
  "plant_id": 59513,
  "plant_name": "Meadows PV 1",
  "monthly_data": [
    {
      "year": 2024,
      "month": 1,
      "net_gen_mwh": 892,
      "capacity_factor": 0.060,
      "peer_median_cf": 0.133,
      "gap_pct": -0.549
    },
    ...
  ]
}
GET/api/plants/:id/hail

Hail events within 50km of a plant, sorted by date descending. Full event history available for top-20 distress plants.

ParameterTypeDescription
formatstringjson (default) or csv
Example
curl "https://solar-distress-map.vercel.app/api/plants/66410/hail"
Response
{
  "plant_id": 66410,
  "plant_name": "Grizzly Ridge Solar",
  "event_count": 59,
  "max_hail_in_24mo": 2.75,
  "events": [
    { "event_date": "2025-05-28", "mag_in": 2.75, "lat": 31.57, "lon": -98.24 },
    ...
  ]
}
GET/api/distress/top

Top N plants by distress score nationally or within a region.

ParameterTypeDescription
nnumberNumber of plants (default 20, max 200)
statestringFilter by state
nerc_regionstringFilter by NERC region
min_scorenumberMinimum distress score
formatstringjson (default) or csv
Example
curl "https://solar-distress-map.vercel.app/api/distress/top?nerc_region=SERC&n=10"
Response
{
  "plants": [ ... ],
  "total": 10,
  "query": { "n": 10, "nerc_region": "SERC" }
}
GET/api/summary

Aggregate distress statistics by NERC region: plant count, median gap, % underperforming, flavor breakdown.

ParameterTypeDescription
nerc_regionstringSpecific region, or omit for all regions
formatstringjson (default) or csv
Example
curl "https://solar-distress-map.vercel.app/api/summary"
Response
{
  "regions": [
    {
      "nerc_region": "SERC",
      "plant_count": 323,
      "median_gap_pct": -0.007,
      "pct_gap_lt_neg15": 0.164,
      "pct_gap_lt_neg25": 0.062,
      "avg_distress_score": 22.7,
      "flavor_counts": { "chronic-underperformer": 16, ... }
    },
    ...
  ],
  "national": { ... }
}
MCP Server

For AI assistants.

The MCP server exposes the same dataset as tools for Claude, Cursor, and any MCP-compatible client. No data download — it's a thin client that calls the REST API.

01
Clone and install
git clone https://github.com/goodvibes413/solar-distress-map.git
cd solar-distress-map/mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt   # mcp + httpx only
02
Configure Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "solar-distress": {
      "command": "/path/to/mcp-server/.venv/bin/python",
      "args": ["/path/to/mcp-server/server.py"]
    }
  }
}
03
Or add to Claude Code (CLI)
claude mcp add solar-distress \
  /path/to/mcp-server/.venv/bin/python \
  -- /path/to/mcp-server/server.py
04
Restart and verify

Restart Claude Desktop. Look for the hammer icon (⚒) in the chat input. Ask: “What NERC regions do you have data for?”

MCP Tools

11 tools, plain English.

Claude calls these automatically based on what you ask. You don't invoke them directly.

ToolWhat it doesKey arguments
screen_plantsFilter all 885 plants by any combination of criteriastate, nerc_region, min_gap, min_score, min_mw, distress_flavor, residual_flag
export_screen_to_csvSame filters, always returns CSV. Use when you want a spreadsheet.same as screen_plants
get_plant_detailFull record for a single plant: flags, decomposition, narrativeplant_id
explain_distressPlain-English analysis of what's driving a plant's underperformanceplant_id
get_performance_historyMonthly capacity factor vs peer median time seriesplant_id, months (default 24)
get_hail_timelineHail events within 50km sorted by date, with magnitude in inchesplant_id
get_distress_leadersTop N most distressed plants nationally or within a regionn, state, nerc_region
get_region_summaryAggregate stats per NERC region: count, median gap, % underperformingnerc_region (optional)
compare_plantsSide-by-side comparison of 2-5 plants by key metricsplant_ids[]
find_by_ownerAll plants matching an owner name (partial, case-insensitive)owner_name
get_methodologyFull methodology: peer cohort logic, gap formula, decomposition rulesnone
Demo Workflow

M&A screening in 6 prompts.

What an analyst would spend 2-3 hours doing across EIA, NOAA, and Excel. With the MCP connected, this takes 30 seconds.

01
Screen for solar plants in the SERC region with gap worse than 15% and no curtailment flag
screen_plants(nerc_region='SERC', min_gap=-0.15, curtailment_suspect=False)53 plants sorted by distress score, with owner, state, gap, and flavor
02
Tell me everything about the top plant — what's driving the underperformance?
get_plant_detail() + explain_distress()Full diagnostic with gap decomposition breakdown and narrative summary
03
Show me the 24-month generation trend vs peers
get_performance_history(months=24)Monthly CF vs peer median — Claude describes when the gap started widening
04
Any significant hail events near this plant in the last 2 years?
get_hail_timeline()Event dates, magnitudes in inches, distances from plant
05
Give me a CSV of all 53 SERC plants I can open in Excel
export_screen_to_csv(nerc_region='SERC', min_gap=-0.15)24-column CSV with all fields + flattened gap decomposition columns
06
Draft an acquisition inquiry email to the operator
(no tool call — Claude uses data from previous steps)Personalized outreach referencing the specific gap, timeframe, and plant context