Solar Resource & PVWatts Yield Scorer — Batch Site Screening
Pricing
from $4.40 / 1,000 results
Solar Resource & PVWatts Yield Scorer — Batch Site Screening
Batch solar resource assessment via NREL PVWatts v8. Lat/lon list in; annual/monthly kWh yield, capacity factor, irradiance, and a solar-worthiness class out. The resource-quality layer for solar, storage, and data-center siting.
Pricing
from $4.40 / 1,000 results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Share
Score a list of coordinates for solar potential in one call, using NREL PVWatts v8. Each site returns modelled annual AC production, specific yield, capacity factor, a full monthly production profile, the solar resource, the identity and distance of the weather station behind the number, and every assumption echoed back so the result is reproducible.
This is a modelled screening estimate, not a bankable energy yield assessment. PVWatts runs a typical-meteorological-year simulation on an assumed-unshaded array with a single generic loss percentage, using weather from the nearest NSRDB grid cell rather than the site itself. Use it to rank and shortlist. Commission a measured, shade-analysed P50/P90 assessment before financing, PPA pricing or construction.
What v1.1 fixed (2026-08-01)
The station distance was wrong by a factor of 1,609 on every successful row
NREL documents station_info.distance in metres. v1.0's code comment
asserted miles and multiplied by 1609.34.
Verified live: Phoenix (33.45, −112.07) returns distance: 923, with the
station at (33.45000076, −112.05999756). One hundredth of a degree of longitude
at 33.45°N is ~929 m — so 923 is metres, and the station is essentially the
site's own grid cell. v1.0 published station_distance_m: 1485421 — a station
1,485 km away. Seattle: distance: 756, published as 1,216,660.
station_distance_m now reports metres. This changes an existing field's
value, and it is the one case where that is correct: the old number was wrong.
A live drift assertion re-derives the true distance from the station coordinates
every run and fails if the units ever flip.
The host list led with a dead host
developer.nrel.gov has no A record — nslookup returns the name with no
address and curl fails in 8 ms. developer.nlr.gov (the api.data.gov alias,
developer-nlr-gov.domains.api.data.gov) resolves and serves v8 in ~0.5 s.
v1.0 listed the dead name first and burned three attempts with exponential
backoff on it before every single site. The live host now leads; the other
is retained as a fallback because DNS is not the same everywhere.
A 422 threw away the message that explains it
PVWatts answers an unusable location with HTTP 422 and a populated errors
array: "No climate data found with dataset=nsrdb for location specified",
plus a warnings array saying "try re-submitting with dataset=intl". v1.0
short-circuited on !res.ok, never read either array, retried the other host
pointlessly, and surfaced a bare "HTTP 422". Both arrays are now read and
emitted. v1.0 never read warnings at all.
Failures could not be told apart from bad sites
Every failure path now sets pvwatts_status — ok, rate_limited, error,
malformed, unavailable, invalid_input — and every metric on a non-ok row
is null. A rate-limited or errored site is never a low yield. A fabricated
"poor" silently disqualifies a good site, and the buyer has no way to know.
A 200 with no ac_annual used to emit errors: null and all-null metrics,
asserting nothing had gone wrong; it is now explicitly malformed.
An invalid key, and a total outage, still billed
A bad key returns HTTP 403 API_KEY_INVALID — v1.0 turned that into a billable
blank row per site and reported SUCCESS. It is now a terminal, actionable
failure. Rows are buffered and a billing gate runs before pushData: if not
one site was scored, the run fails and emits nothing.
Data fetched and thrown away
v1.0 requested a response with 7 output arrays/values, 11 station fields, 8
echoed inputs and 3 envelope fields, and emitted 8 of them. Now emitted:
dc_monthly, poa_monthly, station latitude/longitude/elevation/timezone/id/
country/resource-file, the weather data source and vintage, the PVWatts and
SSC versions, and all eight echoed assumptions.
No field was removed or renamed, and the price is unchanged.
The live drift gate
Two real coordinates with known-magnitude answers, checked before any billable row exists. Any failure aborts with nothing emitted and nothing billed.
| Assertion | Live value 2026-08-01 | What it catches |
|---|---|---|
Response envelope has inputs/errors/warnings/version/station_info/outputs | all present | An API contract change |
| Major version is 8 | 8.5.0 | A silent v9 swap (v9 currently 404s) |
| Phoenix yield inside 6,000–8,200 kWh/yr | 7,020.48 | A model or weather-set shift |
| Seattle yield inside 3,600–5,100 kWh/yr | 4,351.56 | The same, at the other end |
| Phoenix ÷ Seattle ≥ 1.35 | 1.613 | A model degraded to a constant or a clamp — passes both magnitude bands, fails here |
station_info.distance matches the geometry to the station coordinates | 923 m vs 929 m computed | The metres/miles flip that v1.0 shipped |
The gate costs two API calls. Against the shared, rate-limited DEMO_KEY that is
a meaningful share of what an auto-QA run has, so the verdict is cached in a
named key-value store for 3 hours and only the first run in each window pays
for it. Rows from a cached verdict say so: drift_gate_status: "passed_cached".
The drift this gate catches -- a host dying, a version bump, a units flip, a
model shift -- moves on the order of days, not minutes.
If the shared DEMO_KEY is rate-limited when the canaries run, the gate is
skipped rather than failed — rate limiting is a documented property of a
shared key, not upstream drift — and every row says so via
drift_gate_status: "skipped_rate_limited".
Who uses this
- Solar developers and EPC site selectors — rank a candidate list before
spending money on any of them;
specific_yield_kwh_per_kwdcis the cross-site metric, andstation_qualitytells you which numbers to trust. - C&I energy managers and rooftop assessors — compare portfolio sites on a consistent tilt/azimuth/loss basis, all echoed on the row.
- Storage and microgrid designers —
seasonality_ratioandwinter_share_pctsize the winter gap that drives storage and generator hours. - Real-estate and data-centre teams — a quick, cheap on-site-generation read.
- AI agents — one call per site batch; see the MCP note below.
Example input
{"assets": [{ "lat": 33.45, "lon": -112.07, "label": "Phoenix AZ" },{ "lat": 35.08, "lon": -106.65, "label": "Albuquerque NM" },{ "lat": 39.74, "lon": -104.99, "label": "Denver CO" },{ "lat": 47.61, "lon": -122.33, "label": "Seattle WA (control)" }],"apiKey": "DEMO_KEY","systemCapacityKw": 4,"moduleType": 0,"arrayType": 1,"tilt": 20,"azimuth": 180,"losses": 14}
Live result from that exact input (2026-08-01):
| Site | AC kWh/yr | Capacity factor | Class | Seasonality | Winter share | Station |
|---|---|---|---|---|---|---|
| Albuquerque NM | 7,116.16 | 20.31% | excellent | 1.61 | 27.2% | 1,438 m |
| Phoenix AZ | 7,020.48 | 20.04% | excellent | 1.54 | 27.3% | 923 m |
| Denver CO | 6,380.03 | 18.21% | good | 1.66 | 26.1% | 1,405 m |
| Seattle WA | 4,351.56 | 12.42% | poor | 4.34 | 15.3% | 756 m |
Albuquerque narrowly beats Phoenix on annual yield — and Seattle's best month produces 4.34x its worst against Phoenix's 1.54x, with only 15.3% of the year's output falling in the four winter months. None of that seasonality was visible in v1.0.
Input fields
| Field | Type | Default | Meaning |
|---|---|---|---|
assets | array (required) | — | [{ lat, lon, label }]. One PVWatts call per site, plus two drift canaries per run. Omitting it fails the run before anything is billed. |
apiKey | string (secret) | prefill DEMO_KEY | Your free key from developer.nrel.gov/signup. DEMO_KEY is shared and rate-limited — fine for a handful of sites. An invalid key fails the run loudly and bills nothing. |
systemCapacityKw | integer | 4 | Nameplate DC size. specific_yield_kwh_per_kwdc normalises across sizes. |
moduleType | integer | 0 | 0 Standard, 1 Premium, 2 Thin film. |
arrayType | integer | 1 | 0 Fixed Open Rack, 1 Fixed Roof, 2 1-Axis, 3 1-Axis Backtracking, 4 2-Axis. |
tilt | integer | 20 | Degrees from horizontal. |
azimuth | integer | 180 | Degrees from north (180 = due south). |
losses | integer | 14 | System-loss percentage; NREL's default is 14. |
Output fields
Every field below is declared in the dataset schema and present on every row.
null always means not obtained — never a low or zero yield.
Site and headline result
| Field | Type | Description |
|---|---|---|
label | string | Your site label. |
lat | number | Site latitude. |
lon | number | Site longitude. |
ac_annual_kwh | number | Modelled annual AC production. |
dc_annual_kwh | number | Modelled annual DC production. |
kwh_per_kw_year | number | Annual AC output per kW DC installed. |
specific_yield_kwh_per_kwdc | number | The same figure under the industry's name — the standard cross-site comparison metric. |
capacity_factor_pct | number | Annual capacity factor. |
solar_class | string | excellent ≥20%, good ≥17%, fair ≥14%, poor <14%, or unknown when no yield was modelled. unknown is never a low score. |
solrad_annual_kwh_m2_day | number | Annual average daily solar radiation. |
poa_annual_kwh_m2 | number | Annual irradiance on the tilted array surface. |
Monthly profile and seasonality
| Field | Type | Description |
|---|---|---|
monthly_ac_kwh | array | Twelve monthly AC output values. |
monthly_dc_kwh | array | Twelve monthly DC output values. |
monthly_solrad | array | Twelve monthly solar-radiation values. |
monthly_poa_kwh_m2 | array | Twelve monthly plane-of-array irradiance values — what tilt and azimuth actually move. |
best_month | object | { name, ac_kwh } for the highest-production month. |
worst_month | object | { name, ac_kwh } for the lowest. |
best_month_name | string | Best month, flattened for CSV. |
best_month_ac_kwh | number | Best-month AC output. |
worst_month_name | string | Worst month, flattened for CSV. |
worst_month_ac_kwh | number | Worst-month AC output. |
seasonality_ratio | number | Best month ÷ worst month. High means thin winter production. |
winter_share_pct | number | Share of annual output across Nov–Feb. |
dc_to_ac_efficiency_pct | number | Annual AC as a percentage of annual DC. |
Weather station — how much to trust the number
| Field | Type | Description |
|---|---|---|
station_distance_m | number | Distance to the NSRDB grid cell / station, in metres. Corrected in v1.1 (see above). |
station_distance_km | number | The same distance in kilometres. |
station_quality | string | site_grid_cell (≤5 km), near (≤25 km), regional (≤100 km), distant (>100 km). |
station_lat | number | Station latitude. |
station_lon | number | Station longitude. |
station_elevation_m | number | Station elevation — a large site/station elevation gap weakens the estimate in terrain. |
station_timezone_utc_offset | number | Station UTC offset, for aligning output with a local load profile or tariff. |
station_id | string | NSRDB station / grid-cell id. Two sites sharing an id share weather data. |
station_resource_file | string | Filename of the NSRDB record used — the most precise citation available. |
station_state | string | Station state as NREL reports it. |
station_country | string | Station country. |
station_city | string | Structurally empty in PVWatts v8. NREL returns "" for NSRDB grid-cell stations at every location tested (Phoenix, Seattle, NYC, Honolulu, Anchorage, San Juan), so this is always null. Retained because removing a published field would break existing pipelines; use station_id, station_lat/station_lon and station_state instead. |
weather_data_source | string | The NSRDB dataset and vintage, e.g. NSRDB PSM V3 GOES tmy-2020 3.2.0. This is the data-vintage field — if NREL moves to a newer TMY it changes here. |
Assumptions echoed back (reproducibility)
| Field | Type | Description |
|---|---|---|
system_capacity_kw | number | Nameplate DC capacity simulated. |
module_type | integer | PVWatts module_type code. |
module_type_label | string | Standard / Premium / Thin film. |
array_type | integer | PVWatts array_type code. |
array_type_label | string | e.g. Fixed - Roof Mounted, 1-Axis Tracking. |
tilt_deg | number | Tilt used. |
azimuth_deg | number | Azimuth used. |
losses_pct | number | System-loss percentage used. |
Status and provenance
| Field | Type | Description |
|---|---|---|
pvwatts_status | string | ok, rate_limited, error, malformed, unavailable, invalid_input. Anything but ok means every metric is null. |
pvwatts_http_status | integer | HTTP status for this site. |
errors | string | Failure text; null when the site scored cleanly. |
pvwatts_warnings | string | The API's own warnings, e.g. "try re-submitting with dataset=intl". |
drift_gate_status | string | passed (canaries ran this run), passed_cached (a verdict from the last 3 hours was reused), or skipped_rate_limited (the canaries could not run, so the model is unverified for this run). |
pvwatts_version | string | Model version, e.g. 8.5.0. |
ssc_version | string | NREL SAM Simulation Core version. |
host_used | string | Which NREL host served the response. |
source_url | string | PVWatts v8 API documentation. |
disclaimer | string | The modelled-estimate disclaimer. |
checked_at | string | UTC timestamp of the run. |
Legitimately empty on a healthy run: errors and pvwatts_warnings are
null when nothing went wrong — good news, not dead columns. Both are pinned by
offline fixtures. To see them populated live, score a location with no NSRDB
coverage, e.g. {"assets":[{"lat":25,"lon":-160,"label":"Mid-Pacific"}]} →
errors: "No climate data found with dataset=nsrdb...",
pvwatts_warnings: "This location appears to be outside the US, try re-submitting with dataset=intl..."pvwatts_status: "error", every metric null.
Use as an MCP tool
Available to AI agents via mcp.apify.com. Hand it a
list of coordinates and it returns one structured, comparable yield row per
site. pvwatts_status, station_quality and drift_gate_status exist so an
agent can distinguish "this site is poor" from "this site was not scored" and
from "the model itself was not verified this run" — the distinctions that
matter when an agent chains a siting decision.
Pricing
Pay per result — $8 per 1,000 rows. One row per scored site. Graduated paid-plan discounts apply automatically. A run that fails the drift gate, the key check or the billing gate emits zero rows and costs zero.
FAQ
Do I need my own key? No. DEMO_KEY is prefilled and works for small
runs. It is shared across all anonymous users, so it rate-limits quickly under
load; when that happens the affected sites are reported as
pvwatts_status: "rate_limited" with every metric null, and if no site could be
scored the run fails and bills nothing rather than emitting empty billable rows.
A free key takes 30 seconds at developer.nrel.gov/signup
and removes the limit.
Operator note: the Actor also reads a key from the NREL_API_KEY (or
DATA_GOV_API_KEY) environment variable. An explicitly supplied apiKey always
wins, but a blank key or the literal DEMO_KEY falls back to it -- so setting that
one secret env var lifts the prefill and every saved task off the shared key
without editing a single stored input. Env vars are read at BUILD time, so it
needs a rebuild to take effect.
Why is Albuquerque above Phoenix? Elevation and lower average temperature. PV modules lose efficiency as they heat up, so a cooler high-desert site can out-produce a hotter one at similar irradiance. That is exactly the kind of result a coordinate-level model exists to surface.
Can I model tracking? Yes — arrayType 2, 3 or 4. The choice is echoed on
every row as array_type_label.
Is this good enough to finance a project? No, and it does not claim to be. It is a screening model on typical-year data with an assumed-unshaded array. It is good enough to rank a hundred sites and pick the five worth surveying.
What if the site is outside the US? PVWatts defaults to the NSRDB dataset,
which is US-focused. Outside coverage you get pvwatts_status: "error" with
the API's own warning suggesting the international dataset — an honest failure
rather than a fabricated zero.
Related actors
- HIFLD Grid Proximity Screener — distance to transmission, substations and the serving utility, balancing authority and ISO/RTO. The natural next question after "does this site have sun".
- Interconnection Queue Tracker — what is already queued nearby, across 7 ISOs.
- Sage-Grouse Siting Screener, Energy Corridor §368 Screener, EPA Nonattainment Air Permit Screener — the siting constraints on the same coordinate.
- Site Due Diligence Bundle — 20 layers for one coordinate in one call.