BLM Solar Siting Screener — 2024 Western Solar Plan DLA Check
Pricing
from $4.40 / 1,000 results
BLM Solar Siting Screener — 2024 Western Solar Plan DLA Check
Screens coordinates against the BLM Solar PEIS analysis: exclusion areas, Solar Energy Zones, DRECP focus and variance lands. Returns the specific exclusion drivers, transmission and slope screens, the plan stage the data actually represents, and a per-layer status so a clear is never a guess.
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
6 days ago
Last modified
Categories
Share
BLM Solar Siting Area Screener - Western Solar Plan
Screen project coordinates against BLM's Solar Programmatic EIS spatial analysis. For each site you get: whether it is in an exclusion area where utility-scale solar is barred, whether it is in a Solar Energy Zone / Designated Leasing Area, whether it is in a DRECP Development Focus Area or variance-process land, the specific exclusion drivers BLM recorded, the transmission-proximity and slope screens, and a per-layer status so a "clear" is never a guess.
Informational screening only. Not an official BLM land-use or solar-eligibility determination and not a land survey.
Vintage: read this before comparing to another tool
The national exclusion layer is BLM's Proposed Plan analysis from the Final
Solar PEIS. Its own layer description reads "the analysis data for the Proposed
Plan of the Bureau of Land Management Final Solar Programmatic Environmental
Impact Statement", and its service metadata reports a last edit of
2024-08-21 - four months before the Western Solar Plan Record of Decision
(December 2024). BLM publishes no national ROD-stage replacement on this ArcGIS
org (checked live 2026-08-01 across all 697 services; the only ROD-stage service
is BLM_CA_DRECP_ROD, which is California-only).
Earlier versions of this actor described its output as the "2024 Western Solar
Plan (Approved RMP Amendments / Record of Decision, December 2024)". That was
wrong about the data. Every row now carries plan_stage,
exclusion_layer_last_updated (read live from the service on every run) and
exclusion_layer_vintage_note, and a live drift assertion fails the run if
BLM republishes the layer or adds a value to the ProposedPlan vocabulary -
which is exactly what a ROD refresh would look like.
ProposedPlan is a closed two-value vocabulary, verified live: Excluded
(9,749,915 units) and Other (216,121 units). "Other" means the analysis did
not exclude the unit; it is not a BLM statement that the unit is available for
application. The raw value is reported verbatim in proposed_plan_class.
What each run asserts before it bills you
Every run, before a single row is emitted:
- all four layers resolve and still publish the fields the mapper reads
- each layer row count is inside a measured band (a re-vintage or truncation fails)
- each layer own
maxRecordCountis read and sent asresultRecordCount, so truncation is detectable ProposedPlanstill has exactly the two published values- a positive canary (Antonito Southeast SEZ, CO) still returns a polygon
- a negative control (downtown Denver) returns nothing from all four layers
- coordinates pass a degree-magnitude check
If any of those checks completes and measures something wrong, the run fails loudly and bills nothing.
A probe that could not be reached is not a probe that failed
BLM's ArcGIS services answer in about 15 milliseconds once a given query has been asked recently, and take 33-48 seconds the first time that exact query is asked - the exclusion layer holds 9,966,036 features, and anything that has to count or list a large slice of it is computed from scratch, then cached. Measured 2026-09-09: the vocabulary check took 46,988 ms and 47,631 ms cold, then 13 ms and 19 ms warm.
Until v1.2 this actor treated "the service did not answer" and "the service answered and the answer was wrong" as the same event, so one slow response could take a healthy run offline even when every other check had measured a perfect value.
Now each check records one of three outcomes - pass, fail (it answered, and the answer was wrong: real drift) or unavailable (it never answered) - and the checks are split by what they actually defend:
| Role | Checks | If it is unavailable |
|---|---|---|
| Load-bearing | every layer resolving and still publishing the columns the classifier reads; the closed ProposedPlan vocabulary; the Antonito Southeast SEZ positive canary | the run fails and bills nothing - nothing about the layers has been confirmed |
| Corroborating | the four row-count bands; the four downtown-Denver negative controls | the run proceeds and says so: every row carries drift_gate_status: verified_degraded and names the check that was not run |
A fail in either group still stops the run. Only unavailable is treated differently, and only
for the corroborating group. The checks run in parallel phases inside a bounded wall clock, with the
load-bearing ones first, so a slow BLM day costs you a cross-check rather than the whole run.
Each load-bearing check gets up to four attempts inside a 195-second budget, and the whole gate is capped at 240 seconds so it can never be the reason a run runs long. Retrying is a hedge, not just a retry: aborting a request does not cancel BLM's server-side computation, so a later attempt collects the result the first attempt paid for.
Those budgets are sized from latency measured on Apify's own compute, not from a developer machine, and that distinction is load-bearing. Measured 2026-09-23: from a home connection the cold vocabulary check costs 32-48 seconds and the hedge lands on the second attempt; from Apify's egress the same check needs more than 122 seconds of accumulated attempt time. An earlier build sized its budget from the local figure, and the result was a healthy BLM being reported as unreachable.
You can prove both paths yourself without waiting for an outage - see simulateOutage below.
Who this is for
- Utility-scale solar developers and land teams ranking candidate parcels on BLM land.
- Renewable energy project finance and diligence teams checking siting risk.
- Right-of-way / permitting consultants preparing an application.
- GIS and land analysts batch-screening a portfolio of sections.
- AI agents wiring a coordinate to a siting classification over MCP.
Example input
{"assets": [{ "lat": 37.0168, "lon": -105.9491, "label": "Antonito Southeast SEZ (CO)" },{ "lat": 36.56, "lon": -114.92, "label": "Dry Lake area (NV)" },{ "lat": 39.7392, "lon": -104.9903, "label": "Downtown Denver (CO) - off-BLM control" }],"maxAssets": 500}
| Input | Type | Notes |
|---|---|---|
assets | array (required) | { "lat": number, "lon": number, "label": string }. One billable row per asset. Omitting it fails the run before anything is billed. |
maxAssets | integer | Safety cap, default 500, max 5000. |
simulateOutage | string | Diagnostic, default none. drift_corroborating_probe forces the row-count and negative-control checks to time out - the run must still SUCCEED with drift_gate_status: verified_degraded. drift_load_bearing_probe forces the layer-metadata and vocabulary checks to time out - the run must FAIL, emitting nothing and billing nothing. Both travel the same code path a genuine outage takes. |
null is not false
false means the layer answered and the answer was negative. null means the
layer did not answer, so nothing is being asserted. Each of the four BLM layers
records its own outcome (exclusion_layer_status, dla_layer_status,
variance_layer_status, dfa_layer_status, plus layers_failed,
layer_errors and partial_result), and solar_developability_verdict reads
NO DETERMINATION rather than a clear whenever a contributing layer is
unavailable. If no asset could be classified the run fails and nothing is billed.
Output fields
One row per screened asset. Every field below is emitted on every row.
| Field | Type | Description |
|---|---|---|
asset_label | string | Caller-supplied label (or asset_N). |
asset_lat | number | WGS84 latitude of the screened point. |
asset_lon | number | WGS84 longitude of the screened point. |
solar_developability_verdict | string | Plain-language classification. Reads NO DETERMINATION, rather than a clear, whenever a contributing BLM layer did not answer. |
in_designated_leasing_area | boolean | True when the point falls inside a BLM Solar Energy Zone / Designated Leasing Area polygon. Null when that layer did not answer - never a confident false in that case. |
dla_name | string | Name of the Designated Leasing Area / Solar Energy Zone, if any. |
dla_state | string | BLM state office administering the DLA. |
dla_status | string | Developable vs Non-developable sub-area within the DLA. |
in_exclusion_area | boolean | True when the BLM Solar PEIS analysis classes the intersecting unit as Excluded, meaning utility-scale solar development is barred there under the mapped analysis. Null when the exclusion layer did not answer. |
exclusion_reason_codes | array | BLM two-digit exclusion category codes that apply to the intersecting unit. Null when the point is not excluded or the exclusion layer did not answer. |
exclusion_reasons | array | Plain-language BLM exclusion reasons decoded from exclusion_reason_codes. |
in_variance_area | boolean | True when the point falls in DRECP variance-process land. Null when that layer did not answer. |
variance_acres | number | Acreage of the intersecting variance polygon. |
in_development_focus_area | boolean | True when the point falls in a DRECP Development Focus Area. Null when that layer did not answer. |
dfa_acres | number | Acreage of the intersecting Development Focus Area. |
dfa_technology | string | Technologies allowed in the DFA. |
within_15mi_transmission | boolean | BLM flag: the intersecting analysis unit is within 15 miles of an existing or planned transmission line. Null when the exclusion layer did not answer. |
slope_ge_10pct | boolean | BLM flag: the intersecting analysis unit has slopes of 10 percent or steeper. Null when the exclusion layer did not answer. |
on_blm_planning_area_land | boolean | True when the point intersected at least one BLM Solar PEIS analysis unit or solar designation polygon. False only when every layer answered and none matched. Null when a layer did not answer. |
state | string | State the point falls in (from BLM data). |
administrative_unit | string | Administering BLM field office. |
area_acres | number | Acreage of the intersecting exclusion-analysis polygon. |
plan_ref | string | The BLM plan and the exact spatial analysis stage behind this row. |
citation | string | Full source citation including the ArcGIS services queried. |
disclaimer | string | Informational-use disclaimer, including the Proposed-Plan vintage caveat. |
source_urls | object | The BLM ArcGIS FeatureServer layers queried. |
layer_errors | array | Error messages from any BLM layer that did not answer, one entry per failing layer; null when every layer answered. |
checked_at | string | ISO 8601 timestamp of the screen. |
error | string | Error message for an invalid coordinate; null otherwise. Per-layer failures are reported in the *_layer_status, layers_failed and layer_errors columns, not here. |
plan_stage | string | Which stage of the BLM Solar PEIS the mapped exclusion analysis represents. The national layer is Proposed-Plan stage (published August 2024), not the December 2024 Record of Decision - BLM publishes no national ROD-stage replacement. |
exclusion_layer_vintage_note | string | Plain-language warning that exclusion boundaries are Proposed-Plan stage and should be confirmed with the administering BLM office. |
exclusion_layer_last_updated | string | Date the BLM exclusion analysis layer was last edited, read live from the service metadata on every run rather than hard-coded. |
proposed_plan_class | string | The ProposedPlan value BLM publishes for the intersecting analysis unit, verbatim. The published vocabulary is Excluded or Other; Other means the analysis did not exclude the unit, which is not the same as BLM declaring it available. |
exclusion_criteria_count | integer | BLM ExCount - how many distinct exclusion categories apply to the intersecting unit. Null when the exclusion layer did not answer. |
exclusion_driver_fields | array | The specific BLM exclusion-driver columns set to Y on the intersecting unit (for example e14d_wsa, e11_vrm_i_ii_az). The layer publishes about 180 of these and previous versions requested none of them. |
exclusion_driver_labels | array | Readable labels derived from the exclusion driver field names, each prefixed with its BLM exclusion category number. Derived, not published by BLM - exclusion_driver_fields carries the authoritative names. |
unknown_exclusion_codes | array | Exclusion category codes present in the BLM ExList that are not in the published category list this actor decodes. Null when every code was recognised. |
transmission_driver_fields | array | BLM context columns showing which existing or planned transmission lines put this unit within 15 miles. |
slope_driver_fields | array | BLM context columns showing which state slope layer flagged this unit at 10 percent or steeper. |
corridor_driver_fields | array | BLM context columns showing whether the unit is in, or within 15 miles of, a designated Section 368 energy corridor. |
within_15mi_designated_368_corridor | boolean | True when BLM flags the unit as within 15 miles of a designated Section 368 energy corridor. Null when the exclusion layer did not answer. |
dla_developable | boolean | True when the intersecting Solar Energy Zone polygon carries a developable status. Null when the SEZ layer did not answer. |
dla_comments | string | Comment text BLM publishes on the intersecting Solar Energy Zone polygon. |
dla_names | array | Every distinct Solar Energy Zone name intersecting the point, since a point can fall in more than one fragmented polygon. |
variance_technology | string | Technology the DRECP variance-process land applies to. |
variance_surface_occupancy | string | Surface-occupancy condition BLM publishes on the DRECP variance polygon. |
sez_flag_on_analysis | boolean | The independent Solar Energy Zone flag carried on the BLM exclusion analysis unit itself, useful as a cross-check against the separate SEZ polygon layer. Null when the exclusion layer did not answer. |
dfa_flag_on_analysis | boolean | The independent DRECP Development Focus Area flag carried on the BLM exclusion analysis unit. Null when the exclusion layer did not answer. |
on_blm_surface | boolean | BLM surface-ownership flag on the intersecting analysis unit. Null when the exclusion layer did not answer. |
exclusion_layer_status | string | Outcome of the BLM exclusion analysis query: ok, unavailable, or not_applicable. |
dla_layer_status | string | Outcome of the BLM Solar Energy Zone query: ok, unavailable, or not_applicable. |
variance_layer_status | string | Outcome of the DRECP variance-land query: ok, unavailable, or not_applicable. |
dfa_layer_status | string | Outcome of the DRECP Development Focus Area query: ok, unavailable, or not_applicable. |
layers_failed | array | Names of the BLM layers that did not answer for this asset. Null when every layer answered. Any classification backed by a failed layer is reported as null, never as a negative. |
partial_result | boolean | True when at least one BLM layer did not answer for this asset, so the row is not a complete screen. |
drift_gate_status | string | verified when every upstream check completed and passed before your sites were touched. verified_degraded when the load-bearing checks passed but a corroborating cross-check could not be reached and was not run. skipped when the gate did not run. A run whose load-bearing checks fail emits no rows at all. |
drift_negative_control_status | string | pass when downtown Denver returned exactly zero features from all four layers, proving the spatial filter is still applied. unavailable means the controls were attempted and BLM never answered - it does not mean the control failed. skipped means they were not run. Deliberately not a boolean: not-checked and checked-and-negative are different facts. |
drift_probes_verified | array | Names of the upstream checks that completed and passed on this run. Null when the gate did not run. |
drift_probes_unavailable | array | Names of checks that were attempted but never answered. An empty array means every check completed. Null when the gate did not run. A check listed here was not run; it did not fail. |
drift_gate_note | string | Plain-language statement of exactly what was and was not confirmed about the BLM layers before your sites were screened. |
Columns that are null on a healthy run
error, layers_failed, layer_errors and unknown_exclusion_codes are null
when everything worked - that is the good news, not a dead column. error is
populated by an invalid coordinate (for example {"lat": 999, "lon": -105});
layers_failed and layer_errors by a BLM outage; unknown_exclusion_codes
only if BLM adds an exclusion category. dla_comments,
variance_surface_occupancy and the DRECP columns are null off a DRECP hit -
use a California Desert coordinate to populate them. All are pinned by offline
fixtures so they cannot rot.
Sources
| Layer | What it provides | Rows (2026-08-01) |
|---|---|---|
BLM_National_Final_Solar_PEIS_Proposed_Plan_Analysis/8 | National exclusion analysis with about 180 named exclusion-driver columns, transmission and slope screens, Section 368 corridor context | 9,966,036 |
Energy_Designations/10 | BLM Solar Energy Zone / Designated Leasing Area polygons | 153 |
Energy_Designations/12 | DRECP Development Focus Areas | 171 |
Energy_Designations/13 | DRECP variance-process lands | 7 |
All keyless, all on the BLM ArcGIS Online org KbxwQRRfWyEYLgp4.
Use as an MCP tool
Available to AI agents through mcp.apify.com. Input is a plain list of
coordinates; every output field carries a description. An agent should read
partial_result before treating a negative as final, and should quote
plan_stage whenever it reports an exclusion result.
Pricing
Pay per result: $8 per 1,000 rows (one row per screened site), with graduated discounts on paid Apify plans. A failed run emits no rows and bills no result events.
FAQ
Is this the Record of Decision map? No, and the actor says so on every row. BLM has not published a national ROD-stage spatial layer; this is the Proposed Plan analysis from the Final PEIS (August 2024).
What does proposed_plan_class = "Other" mean? BLM analysis did not exclude
that unit. It is not a statement that the unit is available for a right-of-way
application - the transmission-proximity and slope screens and field-office
review also apply, and both screens are reported (within_15mi_transmission,
slope_ge_10pct).
Why does a site get several exclusion reasons? Fragmented analysis units can
overlap a point, and a unit can trip several categories. Codes are unioned and
the most restrictive answer wins; exclusion_driver_fields names each specific
BLM column.
My point is on private land. The verdict will say it is outside the analysis footprint. The Western Solar Plan applies only to BLM-administered public land in AZ, CA, CO, ID, MT, NM, NV, OR, UT, WA and WY.
What happens during a BLM outage? The affected layer is unavailable, its
flag is null, partial_result is true, and the verdict reads NO
DETERMINATION. If nothing could be classified the run fails and bills nothing.
Related actors
- Energy Corridor Section 368 Screener - federal energy right-of-way corridors.
- IRA Energy Community Bonus Screener - 10% ITC/PTC adder eligibility.
- HIFLD Grid Proximity Screener - nearest transmission line, substation and serving utility.
- BLM Sage Grouse Siting Screener - PHMA/GHMA habitat constraints.
- Site Due Diligence Bundle - 20 environmental and siting layers in one call.
Informational screening only. Not an official BLM determination and not a land survey. Confirm with the administering BLM field or state office before any siting, leasing or right-of-way decision.