FEMA Disaster Assistance Delta avatar

FEMA Disaster Assistance Delta

Pricing

Pay per event

Go to Apify Store
FEMA Disaster Assistance Delta

FEMA Disaster Assistance Delta

Monitors official FEMA disaster declaration records for the states, incident types and counties you name, and reports only genuine changes: a brand-new declaration or county, or an IA/PA/HM/IH assistance-program flag flipping on an existing record. A day with nothing new is free.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Radu Furtuna

Radu Furtuna

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

FEMA Disaster Assistance Delta Monitor

Durable, informational monitor of the official FEMA Disaster Declarations Summaries feed — the free, public OData feed the U.S. Federal Emergency Management Agency publishes at https://www.fema.gov/api/open/v2/DisasterDeclarationsSummaries. No API key, no account, no proxy, no browser.

This is a mirror-with-diff of a public government dataset, not legal, insurance or eligibility advice. It does not predict damage, does not estimate insurance coverage, and does not decide whether you or your property qualify for any assistance program. It tells you, reliably and cheaply, when FEMA's own declaration records for the states/incident types/counties you name gain a new entry or change an assistance-program flag.

Why

FEMA already publishes every declaration record for free. What it doesn't give you is a durable, per-organization "what's new for me since I last looked" — you either poll the whole feed yourself and diff it client-side, or you don't watch at all. This actor keeps that diff for you: a list of watches (state + optional incident type + optional county), a durable memory of every matching record's state between runs, and a bill only for records that are genuinely new or have a genuinely changed assistance-program flag.

How it works

  1. Each watch fetches every DisasterDeclarationsSummaries record matching its states (required) and incidentTypes (optional) via the API's own $filter — one request per watch per run, no pagination ($top=10000 covers even Texas, the largest state by declaration count, ~5,400 records as of 12.09.2026). An optional county further narrows the result on the client side (matched as a case-insensitive substring against FEMA's designatedArea field, e.g. "Harris" matches "Harris (County)") — FEMA's own exact-match filter on that field is too brittle for free-text user input ("Harris (County)" vs "Harris County" vs "Harris").
  2. The first run for a watch establishes a baseline: every matching record's id and content hash (as reported by FEMA itself — we do not compute our own) are stored, nothing is billed or delivered.
  3. Every later run compares the fresh set against the stored index:
    • a record id never seen before is new (covers both a brand-new declaration and a newly-designated county inside an existing declaration — FEMA gives each state/county combination its own stable id);
    • a record id seen before whose IA/PA/HM/IH assistance-program flags differ from what was stored is flags_changed;
    • a record whose content hash changed but none of the four assistance flags did (e.g. FEMA back-filled a closeout date) is not treated as a billable delta — see "Honest limits" below.
  4. A hard per-watch cap (maxResultsPerWatch) protects you from a single run billing an unbounded backlog — anything over the cap is picked up cleanly on the next run, nothing is lost or double-billed.
  5. Every request also checks FEMA's own reported total count for that filter ($inlinecount=allpages) against what was actually parsed. A mismatch means a truncated/corrupted response, not that records vanished — that watch is reported FAILED (source_access_limited) for that run, its checkpoint is left untouched, and nothing is billed.

Input

{
"monitorId": "my-fema-watch",
"watches": [
{ "watchId": "texas-hurricanes", "states": ["TX"], "incidentTypes": ["Hurricane"], "county": "Harris" }
],
"maxResultsPerWatch": 200,
"notifyOn": "new_alerts",
"webhookUrl": "https://example.com/webhook"
}
  • watches — 1-30 objects, each {watchId, states, incidentTypes?, county?}. states is 1-10 two-letter USPS codes (e.g. TX, CA, PR). incidentTypes is optional (omit/empty = every incident type). county is optional (case-insensitive substring match against designatedArea). A watchId is bound to its filters on first use — reusing the same watchId with different states/incidentTypes/county later fails the run honestly (watch_config_mismatch) instead of silently hiding records under a stale index.
  • maxResultsPerWatch — 1-2000, default 200.

Output

One row per new/changed record: watchId, status (new/flags_changed), recordId, disasterNumber, state, designatedArea, incidentType, declarationDate, iaProgramDeclared, paProgramDeclared, hmProgramDeclared, ihProgramDeclared, sourceUrl. A run that finds nothing new still writes an honest run_summary row to the default dataset (never silently empty).

Billing

Pay-per-event, two named events:

  • disaster-declaration-new — a state/county combination never seen before under this watch.
  • assistance-program-flag-changed — an already-known record's IA/PA/HM/IH flag changed.

Both are deduplicated by recordId:apiHash (the same record can be billed again later if FEMA genuinely changes its assistance flags again, but never twice for the same content). The baseline run establishes history without charging. Failed/blocked runs (source fetch failed, run timed out) are never charged.

Delivery guarantee: at-most-once (not exactly-once)

The right to write a row and to charge for it is granted by a single atomic primitive — one addRequest(uniqueKey) into a dedicated, named claim-journal Request Queue (<prefix>-<monitorId>-claims). Exactly one run ever wins that key. Claim requests are never deleted and never handled: the queue is a permanent journal of irreversible attempts, not a work list.

  • You will never be charged twice for the same record state. That is the guarantee.
  • It is not exactly-once. If a run wins the claim and then dies before the row reaches the dataset (or before the charge completes), that change is lost: it closes as dataset_unknown / charge_unknown and is never re-delivered. We deliberately prefer losing a delivery over double-charging you.
  • Boundary of the guarantee: it holds for as long as the named claim-journal queue exists. Anyone with account access can delete or re-create that queue through the Apify Console/API; a fresh journal starts empty, and previously delivered changes could then be delivered and billed again. That is an inherent limit of any durable storage, not a defect of the protocol.
  • Migration boundary: the guarantee applies from the build that introduced the claim gate onward. Older builds of this actor must not keep running against the same monitorId. That same build also had to shorten the durable storage name prefix (the old one, the full actor name, could not fit Apify's 63-character storage-name limit together with a 40-character monitorId), so a monitor that ran on an older build starts from a fresh baseline once. A baseline is never charged.
  • coverage.claimJournalSize reports the journal's size each run (best-effort; null if the queue's metadata could not be read, and the value lags a few seconds because Apify's totalRequestCount is eventually consistent). Use it to watch growth, not to make decisions.

Honest limits

  • We mirror FEMA's feed as published; we do not predict damage, estimate insurance coverage, or make any eligibility determination — this is an informational monitor of official declaration records, nothing more.
  • A record's content hash can change for reasons unrelated to assistance eligibility (e.g. a closeout date being back-filled weeks later). Only IA/PA/HM/IH flag changes on an already-known record are billed as assistance-program-flag-changed; other silent metadata corrections update our internal cache but produce no row and no charge (see ROADMAP.md, "Deliberate deviations").
  • If the feed is temporarily unavailable, its shape changes, or its own reported record count doesn't match what we actually received, the affected watch reports that honestly instead of silently returning zero results.

Author: OmniCoder (https://t.me/OmniCoder)