EPA Drinking Water Quality Screener - Violations, Lead & PFAS avatar

EPA Drinking Water Quality Screener - Violations, Lead & PFAS

Pricing

from $6.60 / 1,000 drinking water screening results

Go to Apify Store
EPA Drinking Water Quality Screener - Violations, Lead & PFAS

EPA Drinking Water Quality Screener - Violations, Lead & PFAS

Screen any US coordinate for the public water system serving it: SDWA health-based violations, Lead & Copper Rule 90th-percentile results and UCMR5 PFAS detections. Built for Phase I ESA and real-estate due diligence. Never reports a clear result from a source that did not answer.

Pricing

from $6.60 / 1,000 drinking water screening results

Rating

0.0

(0)

Developer

Kyle Maloney

Kyle Maloney

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Give it a coordinate. It tells you which public water system serves that address, what the Safe Drinking Water Act violation record looks like, what the lead and copper 90th-percentile tap results are, and whether PFAS was detected there under EPA's UCMR5 monitoring programme.

Built for Phase I ESA consultants, environmental due-diligence teams, real-estate underwriters and water-utility analysts who need a defensible drinking-water screen per property, in bulk, from authoritative federal sources.

The design rule that matters most: this actor will not tell you a system is clean on a source it never successfully read. null means not checked. false means checked, and the answer is negative. The two are never mixed, and the overall verdict is gated so it can never read LOW while any contributing EPA source was unavailable.


Who it is for

PersonaWhat they get
Phase I ESA consultantA per-property drinking-water screen with the serving PWSID, health-based violation history, lead action-level exceedances and PFAS results - plus an explicit NO_SERVICE_AREA answer when the site is probably on a private well.
Real-estate / lender due diligenceBulk screening of a portfolio in one run, with a water_quality_screen_level and a plain-English screen_reason per address.
Environmental / water engineerLead and copper 90th percentiles dated to their actual monitoring period, treatment-technique violations, formal enforcement history and per-analyte PFAS concentrations.
PFAS litigation and remediationUCMR5 detections per system with maximum concentrations, the analytes involved, a Hazard Index screen, and honest non-detect accounting.
AI agents / MCPA clean, chainable tool: coordinates in, one fully-typed row per location out, with a per-source status on every row so an agent can tell "no finding" from "no data".

Data sources

LegSourceWhat it provides
Service areaEPA ORD Community Water System Service Area Boundaries (ArcGIS, ~44,600 polygons)Point-in-polygon lookup of the serving PWSID
Water systemEPA Envirofacts SDWIS WATER_SYSTEM + GEOGRAPHIC_AREASystem name, type, population served, source water, owner, counties served, utility contact
ViolationsEPA Envirofacts SDWIS VIOLATION + ENFORCEMENT_ACTIONFull SDWA violation history, health-based rollup, formal enforcement
Lead and copperEPA Envirofacts SDWIS LCR_SAMPLE_RESULT joined to LCR_SAMPLE90th-percentile lead and copper results, dated to their monitoring period
PFASEPA UCMR5 occurrence dataset (1,928,117 results, 10,299 systems, 29 PFAS analytes plus lithium)Detections, maximum concentrations, non-detect counts, Hazard Index screen
Code decodingEPA Envirofacts SDWIS REF_CODE_VALUEEPA's own 2,171-row code table, fetched every run so labels cannot go stale

No API key. Every source is public and keyless.


Example input

{
"assets": [
{ "lat": 43.0125, "lon": -83.6875, "label": "Flint MI - lead action level exceedance" },
{ "lat": 40.9793, "lon": -74.1165, "label": "Ridgewood NJ - PFAS detections" },
{ "lat": 39.7392, "lon": -104.9903, "label": "Denver CO - control" }
],
"includeViolations": true,
"includeLead": true,
"includePfas": true,
"violationYears": 10
}

You can also screen systems directly by PWSID, with or without coordinates:

{ "pwsids": ["MI0002310", "NJ0251001"] }

What that example actually returns (verified live 2026-08-04)

LocationSystemVerdictFindings
Flint MIMI0002310 FLINT, CITY OFMODERATE1 health-based violation (Lead and Copper treatment technique), lead 90th 0.0060 mg/L in the period ending 2025-12-31, 0.020 mg/L historically - above the 0.015 action level - PFAS tested and none detected
Ridgewood NJNJ0251001 RIDGEWOOD WATERHIGH249 PFAS detections, PFOA 0.0314 ug/L and PFOS both above the 2024 NPDWR MCL screen, Hazard Index 0.494, 1 health-based violation
Denver COCO0116001 DENVER WATER BOARDLOW128 violations, none health-based, lead 0.0031 mg/L, PFAS tested and none detected, lithium 10.8 ug/L (lithium is not a PFAS)

Three things that are easy to get wrong, and how this actor handles them

1. A non-detect is not zero, and neither is "not tested"

97.1% of UCMR5 results (1,872,227 of 1,928,117) are non-detects. EPA writes them as AnalyticalResultsSign = '<' with an empty value: the analyte was looked for and not found above the minimum reporting level. Publishing that as 0 would be a fabricated clean measurement.

All four states are distinguishable on the row:

Situationpfas_system_testedpfas_detectedpfas_nondetect_count
Tested, PFAS foundtruetruecount of non-detects
Tested, nothing above the reporting leveltruefalsecount of non-detects
Not a UCMR5 sampling systemfalsenull0
The PFAS source failednullnullnull

pfas_min_reporting_level_ugl tells you what a non-detect actually rules out.

2. Lithium is a UCMR5 analyte and is NOT a PFAS

UCMR5 monitors 30 analytes: 29 PFAS and lithium. Denver detected lithium at 10.8 ug/L and no PFAS at all. Counting lithium as a PFAS detection would report Denver as a PFAS-affected system. Lithium has its own fields (lithium_max_ugl, lithium_detected) and can never enter pfas_detected, pfas_detection_count, the MCL screen or the Hazard Index. A live drift assertion re-checks exactly this against Denver on every run.

3. A lead 90th percentile with no date is worse than no number

LCR_SAMPLE_RESULT has no date column. The monitoring period lives in a separate table, LCR_SAMPLE, joined on sample_id. Without that join you can publish a 2016 result as a system's current lead level. This actor performs the join, reports lead_90th_period_start and lead_90th_period_end, and states the join outcome in lead_period_join_status (complete, partial, failed).

lead_90th_mg_l is the most recent monitoring period. lead_90th_max_ever_mg_l and lead_periods_exceeding_action_level carry the history - Flint's latest period is 0.0060 mg/L while its worst on record is 0.020 mg/L, and both matter to a buyer.

A reported 0 is a real EPA value meaning "at or below the laboratory detection limit at the sampled taps" (26-37% of state LCR rows carry it). It is preserved, never nulled, and flagged with lead_90th_reported_zero so nobody reads it as proof that no lead is present.


What each screening level means

LevelMeaning
HIGHAn unresolved health-based violation, a current lead action-level exceedance, a PFAS result above the 2024 NPDWR MCL screen, or a Hazard Index above 1.
MODERATEA health-based violation on record, a historical lead exceedance, lead at or above the 0.010 mg/L trigger level, a copper exceedance, a Tier 1 public notification, or a PFAS detection below the MCL screen.
LOWEvery requested EPA source answered and none reported any of the above.
NO_SERVICE_AREANo EPA-mapped community water system service area covers the location. The site is likely served by a private well, or the boundary is not mapped. This is an answer, not a clear: private wells are not regulated under SDWA and warrant their own investigation.
INCONCLUSIVEAt least one requested EPA source did not answer. Real findings are still reported; absence of a finding on a failed source is not evidence of absence.

This is a screening tool, not a regulatory determination. Verify anything material against the primacy agency and the system's Consumer Confidence Report.

About the PFAS MCL comparison

pfas_mcl_screen_exceeded compares the highest UCMR5 detection against the MCL values published in EPA's 2024 PFAS National Primary Drinking Water Regulation (89 FR 32532): PFOA 0.004, PFOS 0.004, PFHxS 0.010, PFNA 0.010, HFPO-DA 0.010 ug/L, plus a Hazard Index over PFHxS, PFNA, HFPO-DA and PFBS.

It is a screening comparison only. UCMR5 reports single entry-point samples; NPDWR compliance is determined by the primacy agency on a running annual average. This actor never states that a system is in violation of a PFAS MCL. The basis is restated on every row in pfas_mcl_screen_basis.


Output fields

One row per screened location. Every field below is emitted on every row; an unavailable source yields null, never an absent key and never false.

Location and screening verdict

FieldTypeDescription
asset_labelstringYour label for this location, echoed back so results can be joined to your own records.
asset_latnumberLatitude (WGS84) that was screened. Null for a direct PWSID lookup.
asset_lonnumberLongitude (WGS84) that was screened. Null for a direct PWSID lookup.
asset_indexnumberZero-based position of this location in your input array.
lookup_modestringHow the water system was identified: 'coordinate' (point-in-polygon against EPA service-area boundaries) or 'pwsid' (you supplied the PWSID directly).
water_quality_screen_levelstringOverall screening level: HIGH, MODERATE, LOW, NO_SERVICE_AREA or INCONCLUSIVE. INCONCLUSIVE whenever any requested EPA source failed - the verdict can never read LOW on data that was never checked. A screening indicator, not a regulatory determination.
screen_flagsarrayEvery screening flag raised, for example UNRESOLVED_HEALTH_BASED_VIOLATION, LEAD_ACTION_LEVEL_EXCEEDED, PFAS_ABOVE_2024_NPDWR_MCL_SCREEN.
screen_reasonstringPlain-English explanation of the screening level, naming the failed sources when the result is INCONCLUSIVE.
sources_oknumberHow many of the requested EPA sources answered successfully for this location.
sources_requestednumberHow many EPA sources were requested for this location (boundary, water system, violations, lead, PFAS).
sources_failedarrayNames of the EPA sources that did not answer. An empty array means every requested source answered.
sources_not_attemptedarrayEPA sources that were never attempted because an upstream lookup failed - for example the violation, lead and PFAS legs when the service-area lookup itself went down. Distinct from sources_failed and from a source that genuinely had nothing to look up.
data_completebooleanTrue only when every requested EPA source answered and none was skipped by an upstream failure. False means a finding may be missing.

Serving water system (service-area boundary)

FieldTypeDescription
pwsidstringEPA Public Water System Identification number of the system serving this location (2-letter primacy agency code plus 7 characters).
service_area_pws_namestringWater system name as published on the EPA service-area boundary layer.
service_area_primacy_agencystringPrimacy agency (usually the state) on the boundary record.
service_area_population_servednumberPopulation served, as published on the boundary record.
service_area_service_connectionsnumberService connection count, as published on the boundary record.
service_area_typestringType of service area polygon, for example Residential Area or Municipality.
service_area_feature_typestringHow EPA describes the polygon, for example 'Service Area Boundary'.
service_area_data_providerstringWho supplied the boundary to EPA.
service_area_data_provider_typestringCategory of the boundary data provider, for example Private Company or State Agency.
service_area_data_source_urlstringSource URL EPA cites for this boundary.
service_area_model_methodstringModelling method used to derive the boundary, when EPA records one.
service_area_modification_methodstringHow EPA modified the supplied boundary, when applicable.
service_area_method_detailsstringFree-text detail on the boundary derivation method.
service_area_verification_statusstringEPA's verification status for the boundary, when recorded.
service_area_confirmedstringWhether the boundary has been confirmed, as recorded by EPA.
service_area_km2numberArea of the service-area polygon in square kilometres. Useful for judging boundary precision.
service_area_vintagestringDate the boundary was originally created. Boundaries vary widely in age - some date to 2004.
echo_detailed_facility_report_urlstringLink to EPA ECHO's Detailed Facility Report for this system.
boundary_errorstringError message if the service-area lookup failed. Null on success.
service_area_foundbooleanTrue if a mapped EPA service area covers this location, false if none does, null if the boundary lookup failed. False and null are different answers.
service_areas_matchednumberHow many mapped service areas cover this location. More than one is legitimate (a wholesaler plus its purchaser, or overlapping vintages).
all_pwsidsarrayEvery PWSID whose mapped service area covers this location, primary first.
all_pws_namesarrayNames of every matching water system, in the same order as all_pwsids.
boundary_statusstringOutcome of the service-area lookup: 'ok' or 'unavailable'.
boundary_sourcestringAttribution for the service-area boundary layer.
boundary_layer_national_countnumberTotal service-area records in EPA's national layer at run time, measured live as a truncation and drift guard.

Water system record (SDWIS)

FieldTypeDescription
pws_namestringWater system name from EPA SDWIS.
pws_type_codestringSDWIS water system type code (CWS, NTNCWS, TNCWS, NP, U).
pws_typestringDecoded water system type, for example 'Community water system'.
pws_activity_codestringSDWIS activity code (A active, I inactive, and others).
pws_activitystringDecoded activity status, for example 'Active'.
pws_is_activebooleanTrue if SDWIS marks the system active, false if not, null if the water system record was not retrieved.
pws_deactivation_datestringDate the system was deactivated in SDWIS, if applicable.
primacy_agency_codestringTwo-character primacy agency code (usually the state).
primacy_agencystringDecoded primacy agency name.
primacy_typestringWhether primacy is held by the State, EPA or a Tribe.
epa_regionstringEPA region number for the system.
state_codestringState code recorded on the SDWIS water system record.
population_served_countnumberPopulation served, from SDWIS. May differ from the boundary layer's figure.
service_connections_countnumberNumber of service connections, from SDWIS.
owner_type_codestringSDWIS owner type code (F, L, M, N, P, S).
owner_typestringDecoded owner type, for example 'Local government' or 'Private'.
primary_source_codestringSDWIS primary water source code (GW, GWP, SW, SWP, GU, GUP).
primary_sourcestringDecoded primary water source, for example 'Surface water purchased'.
gw_sw_codestringGround water / surface water code.
gw_swstringDecoded ground water or surface water designation.
is_wholesalerbooleanTrue if the system sells water to other systems, false if not, null if not recorded.
is_school_or_daycarebooleanTrue if the system serves a school or daycare, false if not, null if not recorded.
is_grant_eligiblebooleanTrue if EPA records the system as grant eligible.
source_water_protectionbooleanWhether a source-water protection programme is recorded for the system.
outstanding_performerstringEPA 'outstanding performer' designation, when recorded.
pws_addressstringMailing address of the water system's administrative office.
pws_citystringCity of the water system's administrative office.
pws_zipstringZIP code of the water system's administrative office.
pws_admin_namestringAdministrative contact recorded in SDWIS for the utility.
pws_org_namestringOrganisation contact name recorded in SDWIS for the utility.
pws_phonestringUtility contact phone number published in SDWIS.
pws_emailstringUtility contact email published in SDWIS.
season_begin_datestringStart of the operating season for seasonal systems.
season_end_datestringEnd of the operating season for seasonal systems.
counties_servedarrayCounties the system serves, from SDWIS GEOGRAPHIC_AREA.
cities_servedarrayCities the system serves, from SDWIS GEOGRAPHIC_AREA.
zip_codes_servedarrayZIP codes the system serves, from SDWIS GEOGRAPHIC_AREA.
geographic_area_typesarrayKinds of geographic area recorded for the system (County, City, ZIP Code, Tribal, Indian Reservation).
geographic_area_rowsnumberNumber of GEOGRAPHIC_AREA rows retrieved for the system.
water_system_record_foundbooleanTrue if SDWIS holds a WATER_SYSTEM record for this PWSID, false if the lookup succeeded and returned none, null if the lookup failed.
water_system_rows_expectednumberRow count EPA's own COUNT endpoint reported for the WATER_SYSTEM query, used as an exact truncation guard.
geographic_area_statusstringOutcome of the GEOGRAPHIC_AREA lookup: 'ok' or 'unavailable'.
system_errorstringError message if the water system lookup failed. Null on success.
system_statusstringOutcome of the water system leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'.

Safe Drinking Water Act violations

FieldTypeDescription
violations_totalnumberTotal Safe Drinking Water Act violations on record for this system, all years and all categories.
violations_health_basednumberViolations EPA itself flags as health-based (is_health_based_ind = Y): MCL, MRDL and treatment-technique violations. Not derived here.
violations_health_based_in_windownumberHealth-based violations whose compliance period falls inside your violationYears window. Null when the window is disabled.
violations_health_based_unresolvednumberHealth-based violations SDWIS still marks Open. This is the flag most Phase I reviewers care about.
violations_health_based_known_unreturnednumberHealth-based violations with SDWIS compliance status 'Known' - recorded but not returned to compliance. Reported separately from Open rather than lumped in, because EPA distinguishes them.
violations_health_based_returned_to_compliancenumberHealth-based violations SDWIS marks Returned to Compliance.
violations_unresolved_totalnumberAll violations, health-based or not, SDWIS still marks Open.
violations_by_categoryobjectViolation counts keyed by SDWIS violation_category_code (MCL, MRDL, TT, MR, MON, RPT, Other).
violations_by_compliance_statusobjectViolation counts keyed by SDWIS compliance_status_code (O Open, K Known, R Returned to Compliance, I System Inactive).
violations_mclnumberMaximum Contaminant Level violations.
violations_mrdlnumberMaximum Residual Disinfectant Level violations.
violations_treatment_techniquenumberTreatment technique violations.
violations_monitoring_reportingnumberMonitoring and reporting violations (categories MR, MON and RPT combined).
violations_lead_copper_rulenumberViolations under the Lead and Copper Rule family (rule family 350).
violations_public_notification_tier1numberViolations that carried a Tier 1 public notification - the most urgent tier, requiring notice within 24 hours.
violations_earliest_datestringEarliest violation compliance-period date on record.
violations_latest_datestringMost recent violation compliance-period date on record.
health_based_contaminantsarrayDistinct contaminants named on the system's health-based violations, decoded using EPA's own REF_CODE_VALUE table. Null when EPA publishes no description for the codes involved - see health_based_contaminant_codes, which is always populated.
health_based_contaminant_codesarrayRaw SDWIS contaminant codes on the system's health-based violations. Always populated when health-based violations exist. EPA's reference table omits several modern codes (5200, the Revised Lead and Copper Rule contaminant, is one), so the raw code is authoritative and the decoded name may be null.
health_based_contaminants_undecodednumberHow many of the health-based contaminant codes EPA publishes no description for. Non-zero is normal and reflects a gap in EPA's own reference table, not a gap in the violation record.
most_recent_health_based_violationobjectThe most recent health-based violation as a full object (type, category, contaminant, rule, compliance status, dates, public notification tier).
most_recent_health_based_violation_datestringCompliance-period date of the most recent health-based violation.
health_based_violation_detailsarrayUp to maxViolationDetails individual health-based violation records, newest first, each fully decoded.
health_based_violation_details_truncatedbooleanTrue when more health-based violations exist than were included in the details array. Counts above are never truncated.
enforcement_actions_totalnumberNumber of formal SDWIS enforcement actions on record for this system.
most_recent_enforcement_datestringDate of the most recent formal enforcement action.
most_recent_enforcement_typestringDecoded type of the most recent formal enforcement action.
most_recent_enforcement_type_codestringRaw SDWIS enforcement action type code for the most recent action.
violations_rows_expectednumberRow count EPA's own COUNT endpoint reported for the violation query, used as an exact truncation guard.
violations_rows_retrievednumberViolation rows actually retrieved. A shortfall against violations_rows_expected fails the leg rather than publishing a rollup over a subset.
enforcement_statusstringOutcome of the enforcement lookup: 'ok', 'unavailable' or 'skipped'.
violations_errorstringError message if the violation lookup failed. Null on success.
violations_statusstringOutcome of the violations leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'.

Lead and copper (LCR 90th percentile)

FieldTypeDescription
lead_90th_mg_lnumberLead and Copper Rule 90th-percentile tap result. Value for the MOST RECENT monitoring period, in mg/L. Reported by the water system under 40 CFR 141 Subpart I.
lead_90th_period_startstringStart of the monitoring period the reported lead 90th percentile belongs to. EPA publishes the result and the period in separate tables; this actor joins them, because an undated 90th percentile can present a decade-old result as current.
lead_90th_period_endstringEnd of the monitoring period the reported lead 90th percentile belongs to.
lead_90th_result_sign_codestringEPA result sign code on the lead result. 'L' means the value is an upper bound (below minimum detection limits), 'E' means exactly equal to the value reported. Null means EPA published no sign, which is the case on every row observed to date.
lead_90th_below_detectionbooleanTrue when the lead result sign code is 'L' and the reported number is therefore an upper bound rather than a measurement. Null when EPA published no sign code.
lead_90th_reported_zerobooleanTrue when EPA published the lead 90th percentile as exactly 0. That is EPA's encoding of 'at or below the laboratory detection limit at the sampled taps' - it is a reported value, not a missing one, and it is not proof that no lead is present. Between 26% and 37% of state LCR rows carry it.
lead_action_level_mg_lnumberThe federal lead action level, 0.015 mg/L. Constant, included so a row is self-describing.
lead_action_level_exceededbooleanTrue if the most recent lead 90th percentile exceeds 0.015 mg/L, false if it does not, null if the lead leg did not answer or the system has no reported lead result.
lead_90th_max_ever_mg_lnumberHighest lead 90th percentile on record for this system across all monitoring periods.
lead_90th_max_ever_period_endstringEnd of the monitoring period in which the highest lead 90th percentile was reported.
lead_periods_exceeding_action_levelnumberHow many distinct monitoring periods exceeded the lead action level. A system can be compliant today and have exceeded repeatedly in the past.
lead_monitoring_periodsnumberNumber of distinct lead monitoring periods on record.
lead_results_reportednumberNumber of lead 90th-percentile results retrieved for this system.
copper_90th_mg_lnumberCopper 90th-percentile tap result for the most recent monitoring period, in mg/L.
copper_90th_period_startstringStart of the monitoring period the reported copper 90th percentile belongs to.
copper_90th_period_endstringEnd of the monitoring period the reported copper 90th percentile belongs to.
copper_90th_result_sign_codestringEPA result sign code on the copper result ('L' below detection, 'E' exactly equal). Null when EPA published no sign.
copper_90th_below_detectionbooleanTrue when the copper result is an upper bound rather than a measurement. Null when EPA published no sign code.
copper_90th_reported_zerobooleanTrue when EPA published the copper 90th percentile as exactly 0 - a reported value meaning at or below detection, not a missing one.
copper_action_level_mg_lnumberThe federal copper action level, 1.3 mg/L. Constant, included so a row is self-describing.
copper_action_level_exceededbooleanTrue if the most recent copper 90th percentile exceeds 1.3 mg/L, false if it does not, null if the leg did not answer or no result exists.
copper_90th_max_ever_mg_lnumberHighest copper 90th percentile on record for this system.
copper_90th_max_ever_period_endstringEnd of the monitoring period in which the highest copper 90th percentile was reported.
copper_periods_exceeding_action_levelnumberHow many distinct monitoring periods exceeded the copper action level.
copper_monitoring_periodsnumberNumber of distinct copper monitoring periods on record.
copper_results_reportednumberNumber of copper 90th-percentile results retrieved for this system.
lcr_results_totalnumberTotal Lead and Copper Rule sample-result rows retrieved for this system (lead and copper combined).
lead_period_join_statusstringHow well the lead and copper results joined to their monitoring periods: 'complete', 'partial', 'failed', 'no_results' or 'not_attempted'. Anything but 'complete' means some reported values are undated.
lead_period_join_matchednumberHow many result rows were successfully matched to a monitoring period.
lcr_rows_expectednumberRow count EPA's own COUNT endpoint reported for the lead and copper query, used as an exact truncation guard.
lcr_sample_period_errorstringError message if the monitoring-period lookup failed while the results themselves succeeded.
lead_errorstringError message if the lead and copper leg failed. Null on success.
lead_statusstringOutcome of the lead and copper leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'.

PFAS (UCMR5)

FieldTypeDescription
pfas_ucmr5_vintagestringPublication timestamp of the UCMR5 occurrence file actually used, read from its Last-Modified header. The download URL contains '2023-08' but EPA replaces the file in place, so the path is NOT the vintage.
pfas_ucmr5_source_urlstringURL of the EPA UCMR5 occurrence dataset used for the PFAS screen.
pfas_ucmr5_national_rowsnumberTotal UCMR5 result rows indexed on this run, measured live as a truncation guard.
pfas_ucmr5_national_systemsnumberTotal public water systems present in UCMR5 on this run.
pfas_mcl_screen_basisstringPlain statement of what the PFAS MCL comparison is and is not: a screening comparison against EPA's 2024 PFAS NPDWR values, not a compliance determination, because UCMR5 reports single entry-point samples rather than the running annual average used for compliance.
pfas_system_testedbooleanTrue if this system appears in UCMR5 at all, false if it does not (most small systems were not in the UCMR5 sample), null if the PFAS leg failed. False and null are different answers.
pfas_results_countnumberNumber of UCMR5 result rows for this system, detections and non-detects combined.
pfas_analytes_testednumberNumber of distinct UCMR5 analytes this system was tested for (up to 30: 29 PFAS plus lithium).
pfas_sample_pointsnumberNumber of distinct entry-point sample locations UCMR5 holds for this system.
pfas_nondetect_countnumberNumber of UCMR5 results reported as non-detect - the analyte was looked for and not found above the minimum reporting level. A non-detect is NOT zero and NOT untested.
pfas_min_reporting_level_uglnumberLowest minimum reporting level applied to this system's UCMR5 samples, in micrograms per litre. A non-detect only rules out concentrations above this level.
pfas_first_collection_datestringEarliest UCMR5 sample collection date for this system.
pfas_last_collection_datestringMost recent UCMR5 sample collection date for this system.
pfas_detectedbooleanTrue if any PFAS analyte was detected, false if the system was tested and no PFAS was detected, null if the PFAS leg did not answer. Lithium is a UCMR5 analyte but is NOT a PFAS and never sets this true.
pfas_detection_countnumberNumber of individual PFAS detections (excluding lithium) across all of this system's UCMR5 samples.
pfas_analytes_detectedarrayNames of the PFAS analytes detected at this system, alphabetically. Lithium is deliberately excluded.
pfas_max_analytestringPFAS analyte with the highest detected concentration at this system.
pfas_max_uglnumberHighest single PFAS concentration detected at this system, in micrograms per litre.
pfoa_max_uglnumberHighest detected PFOA concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.004 ug/L.
pfos_max_uglnumberHighest detected PFOS concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.004 ug/L.
pfhxs_max_uglnumberHighest detected PFHxS concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.010 ug/L.
pfna_max_uglnumberHighest detected PFNA concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.010 ug/L.
hfpo_da_max_uglnumberHighest detected HFPO-DA (GenX chemicals) concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.010 ug/L.
pfbs_max_uglnumberHighest detected PFBS concentration, in micrograms per litre. PFBS has no individual MCL; it is one of the four Hazard Index analytes.
pfas_hazard_index_screennumberUnitless Hazard Index computed over PFHxS, PFNA, HFPO-DA and PFBS against their published health-based water concentrations. Null when the system was not tested for any Hazard Index analyte.
pfas_hazard_index_screen_exceededbooleanTrue if the Hazard Index screen exceeds 1.0, false if it does not, null if it could not be computed.
pfas_mcl_screen_exceedancesarrayPFAS analytes whose highest detected concentration exceeds the corresponding 2024 NPDWR MCL. A screening comparison only - see pfas_mcl_screen_basis.
pfas_mcl_screen_exceededbooleanTrue if any detected PFAS exceeds its 2024 NPDWR MCL on this screening comparison, false if none does, null if the PFAS leg did not answer.
lithium_max_uglnumberHighest detected lithium concentration, in micrograms per litre. Lithium is a UCMR5 analyte and is NOT a PFAS; it is reported separately so it can never inflate a PFAS count.
lithium_detectedbooleanTrue if lithium was detected, false if the system was tested for lithium and none was detected, null if lithium was not among the analytes tested or the leg did not answer.
pfas_errorstringError message if the PFAS leg failed. Null on success.
pfas_statusstringOutcome of the PFAS leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'.

Provenance and source integrity

FieldTypeDescription
retrieved_atstringUTC timestamp when this run queried EPA.
sdwis_sourcestringAttribution for the tabular data (EPA Envirofacts SDWIS).
actor_versionstringVersion of this actor that produced the row.
drift_statusstringResult of this run's live source-integrity checks: 'ok' or 'degraded'. A 'failed' status fails the run before any row is emitted.
drift_measurementsstringJSON string of every value measured by the live drift checks on this run (national record counts, canary results, UCMR5 vintage and vocabulary), so source health is auditable.

Total: 186 declared fields, every one of them documented above.


Fields that are legitimately null on a healthy run

Some fields are null because the answer is genuinely absent, not because something failed. Each has a recorded input that populates it.

Field(s)Why null, and what populates it
boundary_error, system_error, violations_error, lead_error, pfas_error, lcr_sample_period_errorNull is good news - the source answered. Populate with {"simulateOutage": "lead"} or any other leg name.
copper_90th_*, copper_action_level_exceeded, copper_periods_exceeding_action_levelMost systems report lead only. Populate with {"pwsids": ["RI1583829"]} - 8 copper results to 2 lead.
health_based_contaminantsEPA's own REF_CODE_VALUE table omits several modern codes, so the decoded name can be null while health_based_contaminant_codes is populated. See below.
lead_90th_result_sign_code, lead_90th_below_detection, and the copper equivalentsEPA publishes no result sign on any LCR row observed to date. The vocabulary (E equal, L below detection) is asserted live, so the day it starts appearing the value is carried through rather than ignored.
pws_deactivation_date, season_begin_date, season_end_date, outstanding_performerOnly populated for deactivated, seasonal or EPA-designated systems.
hfpo_da_max_ugl, pfna_max_ugl and the other per-analyte maximaNull when that analyte was never detected at the system. pfas_analytes_tested confirms it was looked for.
sources_failed, sources_not_attemptedEmpty arrays on a healthy run.
zip_codes_served, cities_servedMany states report only the county in SDWIS GEOGRAPHIC_AREA.

A known gap in EPA's own reference table

REF_CODE_VALUE is EPA's authoritative code decoder and this actor fetches it on every run - but it is materially incomplete for newer SDWIS codes. Measured live on 2026-08-04 across Michigan's 63,401 violations: 18 alphanumeric violation codes are absent from it (3A alone appears 22,955 times), along with 8 contaminant codes including 5200 and rule code 351, the Revised Lead and Copper Rule codes.

This actor never invents a label. The raw code is always emitted next to the decoded name, health_based_contaminants_undecoded counts how many labels EPA does not publish, and every undecoded code is listed in the run log. The one documented exception is violation_category_code MON and RPT, which live data carries and EPA's table omits; those two are supplied locally and the substitution is stated in the source.


Live source-integrity checks

Offline tests cannot see a silently degraded upstream, because the fixtures were shaped from the degraded data. Every run therefore performs 23 live drift assertions before a single billable row is emitted, and logs every measured value. A critical failure fails the run and bills nothing.

Among them:

  • The boundary layer resolves, still exposes all 20 fields this actor requests, holds a national record count in band (44,615 measured), and answers in WGS84.
  • Positive canary: the point 43.0125, -83.6875 must resolve to MI0002310.
  • Negative control: a point in the open Pacific must match no service area. This is what catches a query that silently matches nothing.
  • The legacy SDWIS table names must still answer, with national counts in band (WATER_SYSTEM 434,040, VIOLATION 2,098,792, LCR_SAMPLE_RESULT 279,257).
  • MI0002310 must still show a health-based violation and a lead result at or above the action level. NC0392373 must still show PFHxS at or above 0.05 ug/L.
  • The lithium trap: CO0116001 must still show lithium detected and zero PFAS detections.
  • UCMR5 must hold 30 analytes, all classified, with a closed sign vocabulary and units that decode to the micro sign rather than U+FFFD.
  • The UCMR5 vintage must come from the file's Last-Modified header. The download URL contains 2023-08, but EPA replaces the file in place - the copy served on 2026-08-04 was last modified 2026-02-12. The real vintage is published on every row in pfas_ucmr5_vintage.

Failure behaviour

SituationWhat happens
No usable assets or pwsidsThe run FAILS with a message naming the field and an example value. Nothing emitted, nothing billed.
A coordinate is out of rangeThat location is rejected, named in the log, and no row is emitted for it - you are not billed for an unusable input. Other locations still run.
One EPA source is downThe row is still emitted with that leg's fields null, <leg>_status set to unavailable, and the verdict forced to INCONCLUSIVE.
Every source is down for every locationBuffered rows are discarded before any push and the run FAILS. Zero rows, zero billable events.
A live drift assertion fails criticallyThe run fails before the first push. Zero rows, zero billable events.
Upstream answered and there is genuinely nothingSUCCEEDED with an honest row - for example NO_SERVICE_AREA, or a system with zero violations.

You can prove any of this yourself with the simulateOutage input.


Performance and the PFAS cache

UCMR5_All.txt is 308,819,534 bytes uncompressed. It is streamed and filtered rather than materialised, and the per-system index is cached in a named key-value store keyed to the file's Last-Modified vintage. The first run in a new vintage rebuilds the index in about 40 seconds; later runs reuse it. EPA republishing the file invalidates the cache automatically - no manual refresh is needed, though refreshPfasCache: true forces one.

Set includePfas: false to skip that leg entirely when PFAS is out of scope.


Use as an MCP tool

This actor is callable by AI agents through mcp.apify.com. Every output field carries a description, and the per-source status fields let an agent distinguish "EPA reports no health-based violations" from "the violation source did not answer" - a distinction most tools collapse, and one that matters a great deal in an environmental screen.


Pricing

Pay per result: $12 per 1,000 rows, one row per screened location. Graduated paid-plan discounts apply (Bronze 20%, Silver 30%, Gold 45%, Platinum 60%, Diamond 70%).

A run that fails loudly emits zero rows and bills zero result events.


Same buyer, same due-diligence workflow:

  • EPA Contaminated Site Screener - Superfund, RCRA, USTs, spills and ECHO enforcement within a radius of the same coordinate.
  • Site Due Diligence Bundle - a 20-layer federation for one coordinate in one call, covering contamination, wetlands, flood, seismic and habitat.
  • EPA Impaired Waters 303(d) Screener - surface-water impairments and TMDLs near the site; the surface-water counterpart to this actor's drinking-water view.

FAQ

How do I find out which water utility serves an address? Pass the address coordinates in assets. The actor returns pwsid, pws_name, population_served_count, the counties served, and the utility's published contact details.

How do I check a water system for lead? lead_90th_mg_l is the most recent 90th-percentile tap result with its monitoring period. lead_action_level_exceeded compares it to the 0.015 mg/L federal action level. lead_90th_max_ever_mg_l and lead_periods_exceeding_action_level give the history, which is what a system like Flint makes clear: currently compliant, historically not.

How do I check whether a water system has PFAS? pfas_detected, pfas_analytes_detected and pfas_max_ugl come from EPA's UCMR5 monitoring. pfas_mcl_screen_exceeded compares detections against the 2024 NPDWR MCL values as a screen. If pfas_system_tested is false, the system was simply not in the UCMR5 sample - that is not a clean result.

What if my site is on a private well? You get a NO_SERVICE_AREA row saying so explicitly. SDWA does not regulate private wells, so a drinking-water screen from SDWIS cannot cover the site.

Does this cover every US water system? SDWIS covers all regulated public water systems. The service-area boundary layer maps roughly 44,600 of them, so a coordinate lookup can miss a system whose boundary EPA has not published. Use pwsids to screen a specific system directly when you already know it.

Is a lead result of 0 mg/L proof there is no lead? No. EPA uses 0 to mean "at or below the laboratory detection limit at the sampled taps". The row flags it with lead_90th_reported_zero.

Why does a row say INCONCLUSIVE when most of it looks fine? Because at least one requested EPA source did not answer. The findings you can see are real; what you cannot see was never checked. sources_failed names the legs, and data_complete is false.

Can I use this for a Phase I ESA? It is designed for that screening step and reports its own data completeness on every row. It is a screening tool, not a determination - verify material findings with the primacy agency and the system's Consumer Confidence Report.

Is an API key needed? No. Every source is a public, keyless EPA endpoint.