One REST call. Hazards + tax for any California parcel.
Three endpoints share one input shape (APN + county FIPS, or address). Use /v1/parcel for the unified envelope (recommended), or call /v1/tax and /v1/hazards individually if you only need one half.
Endpoints
/v1/parcelRecommended
Unified envelope — NHD + Tax in one call.
/v1/tax
California tax decode only. Drop-in replacement for the legacy CTD Tax Summary JSON.
/v1/hazards
Statewide NHD layers only — FEMA / CAL FIRE / CGS / DWR / CalGEM / EPA.
Auth
Every request must carry an X-API-Key header. Keys start with tl_live_ (or tl_test_ for sandbox) and are issued from the customer dashboard. Treat them like passwords — we hash with SHA-256 before storage and show the raw key once. Rate limit: 100 requests per minute per key.
POST /v1/parcel — unified
The headline endpoint. One call returns property snapshot, statewide hazards, and tax decode. Internally fans out to the tax and hazards lookups in parallel — either half failing degrades to a partial response (the failure shows up in errors[] rather than blowing up the whole call).
curl -X POST https://nhddata.com/api/v1/parcel \
-H "Content-Type: application/json" \
-H "X-API-Key: tl_live_xxxxxxxxxxxx" \
-d '{
"apn": "4286-022-011",
"county_fips": "06037"
}'{
"property": {
"apn": "4286-022-011",
"county": "Los Angeles",
"owner_name": "JAMES E PRICE REVOCABLE TRUST",
"year_built": 1921,
"property_type": "DUPLEX",
"formatted_address": "27 SUNSET AVE, VENICE, CA 90291",
"centroid": { "lat": 33.993301, "lng": -118.478 },
"source": "ATTOM"
},
"resolved_geom": "polygon",
"hazards": [
{ "layer": "fema_flood", "inside_centroid": false, "parcel_intersects": false, "pct_overlap": 0, "severity": "none", "details": { "zones": [] }, "source": "FEMA NFHL" },
{ "layer": "calfire_fhsz", "inside_centroid": true, "parcel_intersects": true, "pct_overlap": 1.0, "severity": "very_high", "details": { "lra_class": 3, "jurisdiction": "LRA" }, "source": "CAL FIRE FRAP" },
{ "layer": "cgs_alquist_priolo", "inside_centroid": false, "parcel_intersects": false, "pct_overlap": 0, "severity": "none", "details": { "mapped": false }, "source": "CGS" },
{ "layer": "cgs_seismic_hazard", "inside_centroid": true, "parcel_intersects": true, "pct_overlap": 0.42, "severity": "high", "details": { "liquefaction_in_zone": true, "landslide_in_zone": false }, "source": "CGS" },
{ "layer": "cgs_tsunami", "inside_centroid": true, "parcel_intersects": true, "pct_overlap": 1.0, "severity": "high", "details": { "county": "Los Angeles" }, "source": "CGS Tsunami Maps" },
{ "layer": "dwr_dam_inundation", "inside_centroid": false, "parcel_intersects": false, "pct_overlap": 0, "severity": "none", "details": {}, "source": "DWR DSOD" },
{ "layer": "calgem_wells", "inside_centroid": false, "parcel_intersects": false, "pct_overlap": null, "severity": "none", "details": { "count": 0 }, "source": "CalGEM WellSTAR" },
{ "layer": "epa_environmental", "inside_centroid": false, "parcel_intersects": false, "pct_overlap": null, "severity": "low", "details": { "total": 0 }, "source": "EPA / CalEPA" }
],
"tax": {
"annual_tax": 19648.66,
"direct_charges": [
{ "name": "SANTA MARGARITA WTR DIST CFD 2013-1", "agency_code": "0734", "amount": 1814.35, "kind": "cfd", "bond_payoff_year": 2042, "cfd_id": "2013-0234" }
],
"has_mello_roos": true,
"has_1915_bond": false,
"has_pace": false,
"data_sources": { "property": "ATTOM", "tax_bill": "la_county_ttc_scrape", "cfd_master": "CDIAC YFSR" },
"cache_hit": true
},
"queried_at": "2026-05-21T16:32:14Z"
}Parcel polygon vs centroid resolution
California Civil Code §1103.2 requires natural-hazard disclosure when "the property is located in" a hazard zone — meaning the parcel polygon, not its centroid. A legacy centroid-only lookup silently under-discloses on larger lots where the rooftop sits on safe ground but the lot boundary crosses a FEMA flood, CAL FIRE FHSZ, fault, or dam-inundation polygon.
NHDdata runs ST_Intersects(hazard.geom, parcel.geom) directly against the parcel boundary when one is available, and also reports the fraction of the lot that lies inside each hazard (pct_overlap, 0–1). This is the same polygon-vs-polygon model used by every legitimate NHD provider.
Each response surfaces three fields per layer:
inside_centroid— true if the parcel centroid is inside the hazard polygon.parcel_intersects— true if any part of the parcel polygon intersects. The legally-defensible disclosure flag.pct_overlap— fraction (0–1) of the parcel area inside the hazard. Null when the layer is proximity-based (oil & gas wells, EPA sites) or when polygon mode is unavailable.
The top-level resolved_geom field tells you which mode ran (polygon or centroid) — when ATTOM doesn't expose a polygon for the lot, the response degrades to centroid mode and flags the degradation explicitly.
POST /v1/tax
Drop-in replacement for the legacy CTD Tax Summary JSON. Same wire shape, decoded direct charges (Mello-Roos / 1915 / PACE / other), CDIAC bond-payoff enrichment, and the 5,500+ agency fund-code dictionary.
curl -X POST https://nhddata.com/api/v1/tax \
-H "Content-Type: application/json" \
-H "X-API-Key: tl_live_xxxxxxxxxxxx" \
-d '{ "apn": "4286-022-011", "county_fips": "06037" }'POST /v1/hazards
Returns the eight statewide NHD layers for a parcel or arbitrary lat/lng point. Same envelope as the hazards[] array inside /v1/parcel: per-layer parcel_intersects, inside_centroid, pct_overlap, severity, and details (layer-specific shape).
curl -X POST https://nhddata.com/api/v1/hazards \
-H "Content-Type: application/json" \
-H "X-API-Key: tl_live_xxxxxxxxxxxx" \
-d '{
"apn": "4286-022-011",
"county_fips": "06037"
}'
# or lat/lng directly (no parcel resolution)
curl -X POST https://nhddata.com/api/v1/hazards \
-H "X-API-Key: tl_live_xxxxxxxxxxxx" \
-d '{ "point": { "lat": 33.993301, "lng": -118.478 } }'Billing & credits
NHDdata runs on a prepaid credit balance. You buy credits up front via Stripe Checkout from /dashboard/billing, the balance loads instantly, and each API call atomically debits the balance. No invoices, no Net 30, no AR.
What each call costs
| Endpoint | Cost per unique APN per month |
|---|---|
POST /v1/parcel | $10.00 — NHD + Tax bundled |
POST /v1/tax | $5.00 — tax-only |
POST /v1/hazards | $5.00 — NHD-only |
If you call a singleton then later call /v1/parcel for the same APN in the same calendar month, you pay the difference, not the full bundle again. The bundled call is strictly cheaper than buying singletons separately.
Cache hits don't consume credits
Every successful lookup is cached per-APN for 30 days. Cache hits within that window return instantly and do not debit your balance. The response includes "cache_hit": true so you can confirm it on your side.
When you run out
A request that would drop your balance below zero is rejected with HTTP 402 Payment Required and a JSON body of the shape:
{
"error": {
"code": "insufficient_credits",
"message": "Your credit balance is exhausted. Top up at https://nhddata.com/dashboard/billing.",
"balance_cents": 0,
"required_cents": 1000
}
}Your existing API key stays valid — top up the balance from /dashboard/billing and the next call goes through. Pricing details at /pricing.
Errors
| Code | Reason |
|---|---|
| 401 | Invalid, missing, or revoked X-API-Key. |
| 402 | Insufficient credits. Top up balance at /dashboard/billing. |
| 404 | Parcel not found — APN unknown to ATTOM, or address not geocodable. |
| 422 | Malformed body — missing apn/address, bad FIPS, etc. |
| 429 | Rate limit exceeded — 100 requests per minute per key. |
| 500 | Upstream failure. Response includes `error_id` + `retry_after`. |
| 501 | Endpoint understood but not yet implemented for this input shape. |
Hazard layer coverage
| Layer | Scope | Status |
|---|---|---|
| FEMA flood (NFHL) | Statewide | Live |
| CAL FIRE FHSZ | Statewide | Live |
| CGS Alquist-Priolo (fault zone) | Statewide | Live |
| CGS seismic hazard (liquefaction + landslide) | Statewide | Live |
| CGS tsunami inundation | Coastal CA | Live |
| DWR dam inundation | Statewide | Live |
| CalGEM oil/gas wells | Statewide | Live |
| EPA environmental sites | Statewide | Live |
Tax direct-charge coverage
| County | Status |
|---|---|
| Orange (06059) | Live — direct-charge scraper |
| San Diego (06073) | Live — special-assessment lookup |
| Riverside (06065) | Live — Grant Street GovHub |
| San Bernardino (06071) | Live — Grant Street GovHub |
| Los Angeles (06037) | Live — TTC scraper |
| Contra Costa (06013) | Live — Grant Street GovHub (JSON) |
| Butte · Colusa · Del Norte · El Dorado · Glenn · Humboldt · Imperial · Lake · Madera · Mariposa · Merced · Modoc · Mono · Monterey · Napa · Nevada · Placer · Plumas · San Benito · San Joaquin · Shasta · Siskiyou · Sonoma · Stanislaus · Tehama · Trinity · Tulare · Tuolumne · Yolo · Yuba | Live — MPTS / Manatron |
| Fresno (06019) | Live — custom Kendo scraper |
| Alpine (06003) | Live — Pacific Sky County Tax Retriever |
| Sacramento · Santa Clara · San Francisco · San Mateo · Alameda · Santa Barbara · Ventura · San Luis Obispo · Marin | Partial — scrapers built, awaiting per-county rollout (Playwright pipeline) |
| All 58 counties | ATTOM property snapshot live; per-APN direct-charge coverage rolling out |
Full live matrix at /coverage. For counties without a direct-charge scraper yet, the response still returns property + assessed values from ATTOM and sets data_sources.tax_bill: "scraper_pending".
Changelog
- 2026-05-21Upgraded hazard resolution from centroid-only to parcel polygon. /v1/parcel and /v1/hazards now return resolved_geom plus per-hazard parcel_intersects and pct_overlap when ATTOM returns a parcel boundary.
- 2026-05-20Added /v1/parcel (unified) and /v1/hazards endpoints. NHD layers now exposed via REST.
- 2026-05-19Voter-approved bond enrichment added to tax response (voter_approved_bonds[]).
- 2026-05-15Per-APN tax-bill scrapers shipped for Orange, San Diego, Riverside, San Bernardino.
- 2026-05-10Initial /v1/tax release. CDIAC YFSR bond-payoff enrichment + 5,500-row agency dictionary.