# Bulk Property Flood-Zone Screening (`afkdigital/bulk-property-flood-zone-screening`) Actor

Batch FEMA NFHL coordinate screening with flood zones, map panels, coverage and error statuses. JSON/CSV. Point screening, not an insurance determination.

- **URL**: https://apify.com/afkdigital/bulk-property-flood-zone-screening.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** Real estate, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$15.00 / 1,000 successfully screened unique coordinates

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bulk Property Flood-Zone Screening

Screen batches of property coordinates against FEMA's official **effective National Flood Hazard Layer (NFHL)**. Return intersecting flood zones, FIRM panels and digital-map availability evidence as JSON or CSV. No geocoding account, paid geocoding service, proxy or FEMA API key is required.

**Live-verified on November 5, 2026.** Source outages are returned as errors, never as reassuring flood-risk results. The default two-point example completed in under 10 seconds during validation.

### Input

Supply exactly one of `properties` or `csv`. Coordinates must be WGS84 latitude/longitude (EPSG:4326), in degrees. The service receives x=longitude and y=latitude with `inSR=4326`. Numeric strings from CSV are accepted; blanks, booleans, NaN, infinity and out-of-range coordinates are rejected without swapping axes. Every row requires a nonempty propertyId (maximum 256 characters). Postal addresses, parcels and polygons are outside version 1.

```json
{
  "properties": [
    {"propertyId":"houston-downtown","latitude":29.7604,"longitude":-95.3698},
    {"propertyId":"miami-beach","latitude":25.7907,"longitude":-80.13}
  ],
  "maxResults":100,
  "maxChargeUsd":1.5,
  "requestsPerSecond":2,
  "timeoutSecs":12,
  "maxRetries":2,
  "maxRuntimeSecs":240
}
```

For CSV, remove `properties` and paste CSV text in `csv`, including the `propertyId,latitude,longitude` header. UTF-8 BOM, quoted identifiers, commas inside quoted fields and leading-zero IDs are supported. Example: `{"csv":"propertyId,latitude,longitude\n001,29.7604,-95.3698","maxResults":100}`.

Maximum 10,000 JSON rows, 2 MB CSV, 10,000 output rows. The default maxResults is 100; each input row produces one output row, including repeats and invalid inputs. Use a smaller maxResults for a bounded sample. For large batches increase the Apify timeout as well as maxRuntimeSecs. The default two-point example is designed to finish below five minutes; a source failure does not count as a successful screening.

### Sources and spatial relationships

Official effective REST service: https://hazards.fema.gov/arcgis/rest/services/public/NFHL/MapServer

| Layer | Purpose | Important fields |
|---|---|---|
| 28 — Flood Hazard Zones | Intersecting zone polygons | FLD_ZONE, ZONE_SUBTY, FLD_AR_ID, DFIRM_ID |
| 3 — FIRM Panels | Intersecting map panels and their dates | FIRM_PAN, EFF_DATE, DFIRM_ID |
| 0 — NFHL Availability | Digital NFHL availability evidence | Original attributes retained |

Every run first verifies layer names, polygon geometry and required fields. It saves the observed service CRS, field names and verification time in SOURCE_METADATA. FEMA documents native NFHL storage in NAD83 / GRS80; the REST server projects the explicitly identified EPSG:4326 input. No local projection guess is made. Query requests use exact point `esriSpatialRelIntersects`, without an artificial buffer or snapping.

FEMA mapping covers available effective digital studies in the United States and territories; it is not a complete global map, and not every effective FIRM has digital GIS coverage. No hardcoded continental-US box discards Alaska, Hawaii, Puerto Rico or other territories.

The row contains **all** zone matches, panel matches and availability matches in separate arrays. These are independently intersecting layers at the same point. The Actor never invents a one-to-one zone-to-panel relationship or chooses an arbitrary winning zone. `panel_matches` keeps each panel and its own effective date; the top-level arrays are summaries. A panel date is not a guarantee that every intersecting zone has that same revision date. Original attributes and object IDs are retained for audit. This version does not independently resolve LOMA letters, parcels, buildings, local surveys or pending/preliminary maps.

Boundary intersections may return multiple zones or panels. A single returned feature cannot establish that a location is away from a boundary; source scale, coordinate precision and positioning accuracy matter.

### Output

| Field | Meaning |
|---|---|
| property_id, latitude, longitude | Caller ID and validated point; invalid coordinates become null |
| flood_zone, zone_subtype | Arrays of distinct source labels; empty means no label returned |
| map_panel, effective_date | Summary arrays; panel/date associations remain in panel_matches |
| coverage_status | Digital-data evidence, described below |
| query_status | Screening outcome, separate from coverage |
| source_reference | Official layer URL, ID, match count, input CRS and spatial relation |
| checked_at | UTC ISO timestamp |
| zone_matches, panel_matches, availability_matches | Independent related feature arrays, with original attributes |
| billing_status, error_code | Charging outcome and sanitized error classification |
| input_index | Stable zero-based position in the input, including repeated points |

Query statuses: `success`, `success_no_match`, `invalid_input`, `service_error`, `budget_exhausted`, `runtime_limit`, `billing_unconfirmed`.

Coverage statuses: `zone_data_available`, `digital_coverage_no_zone_match`, `panel_only_no_zone_match`, `no_digital_coverage_match`, `unknown`.

A successful empty layer query is not a technical error. No digital-coverage match is not proof that no official map exists. Service errors mean coverage is unknown. **None of these statuses means safe or risk-free.**

Results appear in the default Dataset (JSON and CSV exports). OUTPUT.json and OUTPUT.csv in the run's key-value store provide complete exports; nested arrays are JSON-encoded in CSV cells and potentially executable spreadsheet text is neutralized. SUMMARY contains counts and event-cost information. SOURCE_METADATA contains verification results. Runs with technical lookup failures are marked FAILED and retain any partial/error outputs.

### Price and limits

Event: **coordinate-screened — $0.015 per successfully queried unique coordinate ($15 / 1,000)**, including all three required layer queries. There is no Actor-start fee. Platform usage is included in this price; the platform pricing record is authoritative.

A successful query **without a matching zone**, including one without a digital-coverage match, is chargeable: the official service was successfully queried. A coordinate is successful only when all three required layer requests complete and their responses pass validation.

Invalid input, network/service/schema/timeout errors, internal request retries and duplicate coordinates within the same run are not charged. Equivalent numeric coordinate strings deduplicate to the same numeric point; property IDs remain separate. Distinct coordinates are not rounded together. A new independent run may charge again for refreshed results. Durable progress reserves pending charges against the budget and reconciles platform event counts when resumed. Immutable input-indexed results and Dataset prefix checks prevent ordinary append/restart windows from duplicating rows. Uncertain billing is retained as billing_unconfirmed and the run fails for reconciliation instead of charging again. Input changes during a resume are rejected.

Both maxChargeUsd (default $1.50) and Apify's run maximum charge limit apply. Set a zero event budget for no paid lookups. Rows beyond available event budget get budget_exhausted. Default rate is 2 requests/second globally, maximum 4; requests are sequential. Per-request timeout and 0–3 retries are bounded. HTTP 429 and transient 5xx errors use backoff. Truncated GIS responses, malformed responses and ArcGIS errors carried in HTTP 200 are failures.

### Appropriate use

This is **point-based preliminary screening**, not an official flood-insurance determination, a Standard Flood Hazard Determination Form, a whole-parcel decision, a survey or a prediction of future flood probability. It does not determine insurance coverage, required premiums, lending compliance or future inundation. Consult the current official FEMA maps, letters and qualified professionals when those decisions matter. Source zone labels are reproduced without turning them into this Actor's own probability estimates.

FEMA data are credited to the Federal Emergency Management Agency. This independent Actor is not endorsed by FEMA. The fee pays for processing, not exclusive ownership of public data.

### Data use and privacy

Official references:

- FEMA NFHL GIS services and coverage/CRS notes: https://hazards.fema.gov/femaportal/wps/portal/NFHLWMS
- FEMA Map Service Center: https://msc.fema.gov/portal/home
- US government NFHL catalog and government-works license reference: https://catalog.data.gov/dataset/national-flood-hazard-layer-nfhl
- FEMA effective service and individual layer metadata linked above

The government catalog identifies NFHL as public and points to the US government works policy. This does not create warranties, a service availability guarantee or permission to imply an official determination. Live metadata was checked on November 5, 2026: the effective service identified NAD83 (EPSG:4269), the expected three polygon layers, and a service publication note dated October 21, 2026. Its item metadata supplied no additional license text. Older catalog modification dates and service publication notes are not individual map effective dates.

Customer points are sent only to the official FEMA GIS service for lookup and stored in the customer's Apify run input/output/progress stores. Routine logs contain counts and sanitized error codes, without coordinates, property IDs or request URLs. Do not put sensitive addresses in property IDs. Manage/delete run storage using your Apify retention settings. No location analytics or third-party geocoding is added.

### Development

Node.js 22+, `npm ci`, `npm test`, `npm start` with Apify INPUT in local storage. Docker builds run the deterministic test suite. Fixture tests are explicitly synthetic and do not replace live FEMA tests. Live evidence and release-gate results are kept separately in the project evidence folder.

### Verified real examples

These are observed source results from November 5, 2026, not future-risk predictions. Map data may change.

| Point | Returned zone labels | Panel | Panel effective date |
|---|---|---|---|
| Houston: 29.7604, -95.3698 | X — AREA OF MINIMAL FLOOD HAZARD | 48201C0690N | 2017-01-06 |
| Miami Beach: 25.7907, -80.13 | AE | 12086C0317L | 2009-09-11 |
| Official polygon vertex: 25.810070441012, -80.122466394582 | AE and X (3 source polygons retained) | 12086C0336L | 2009-09-11 |
| London: 51.5074, -0.1278 | No zone or digital-coverage match; success_no_match | None | None |

The official boundary vertex was taken from FEMA layer 28 feature OBJECTID 24264652, requested with outSR=4326. Adjacent probes 0.00001 degrees west/east returned AE/X respectively. Another tested vertex returned VE and AE. No single source feature was chosen as a winning determination.

Validation covered multiple zones, overlapping map panels with different dates, actual polygon boundaries and neighboring points, outside coverage, invalid inputs, CSV identifiers, duplicates, a $0.015 cap, an official ArcGIS service error, and successful resurrection with unchanged Dataset rows and charge counts. Twenty-one deterministic tests, including the actual Apify SDK adapter, also passed. Initial development failures were corrected before release.

# Actor input Schema

## `properties` (type: `array`):

Supply this list OR CSV text. Each row needs propertyId, latitude and longitude (WGS84 / EPSG:4326). Remove this field when using CSV.

## `csv` (type: `string`):

Alternative to properties. Paste CSV with propertyId,latitude,longitude headers. Quoted values and UTF-8 BOM supported; maximum 2 MB. Remove properties when using CSV.

## `maxResults` (type: `integer`):

Maximum input rows processed, including invalid rows and repeats. 1–10000.

## `maxChargeUsd` (type: `number`):

Additional event-cost cap. Effective cap also respects Apify run maximum charge. No start fee; successful unmatched queries cost $0.015 too.

## `requestsPerSecond` (type: `number`):

Global sequential request cap, including retries and layer metadata.

## `timeoutSecs` (type: `integer`):

Per-request timeout. Transient errors use bounded retries.

## `maxRetries` (type: `integer`):

Retries after initial request for network errors, timeouts, rate limits and transient server errors.

## `maxRuntimeSecs` (type: `integer`):

Stops starting lookups at this deadline. Remaining rows receive runtime_limit.

## Actor input object example

```json
{
  "properties": [
    {
      "propertyId": "houston-downtown",
      "latitude": 29.7604,
      "longitude": -95.3698
    },
    {
      "propertyId": "miami-beach",
      "latitude": 25.7907,
      "longitude": -80.13
    }
  ],
  "maxResults": 100,
  "maxChargeUsd": 1.5,
  "requestsPerSecond": 2,
  "timeoutSecs": 12,
  "maxRetries": 2,
  "maxRuntimeSecs": 240
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `sourceMetadata` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "properties": [
        {
            "propertyId": "houston-downtown",
            "latitude": 29.7604,
            "longitude": -95.3698
        },
        {
            "propertyId": "miami-beach",
            "latitude": 25.7907,
            "longitude": -80.13
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/bulk-property-flood-zone-screening").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "properties": [
        {
            "propertyId": "houston-downtown",
            "latitude": 29.7604,
            "longitude": -95.3698,
        },
        {
            "propertyId": "miami-beach",
            "latitude": 25.7907,
            "longitude": -80.13,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/bulk-property-flood-zone-screening").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "properties": [
    {
      "propertyId": "houston-downtown",
      "latitude": 29.7604,
      "longitude": -95.3698
    },
    {
      "propertyId": "miami-beach",
      "latitude": 25.7907,
      "longitude": -80.13
    }
  ]
}' |
apify call afkdigital/bulk-property-flood-zone-screening --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/bulk-property-flood-zone-screening"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HTo8muGNido0JAlzG/builds/DIgedOFCReaEE2jOd/openapi.json
