HUD Affordable Housing Explorer: LIHTC + Multifamily + Public
Pricing
from $3.30 / 1,000 results
HUD Affordable Housing Explorer: LIHTC + Multifamily + Public
Search HUD LIHTC, FHA-insured multifamily, and public housing layers as one unified property feed. Filter by state, city, program, and minimum units; each row has units, low-income units, owner/manager, lat/lon, and an Opportunity Zone flag. Lead lists for LIHTC investors, developers, lenders.
Pricing
from $3.30 / 1,000 results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
1
Monthly active users
5 days ago
Last modified
Share
HUD Affordable Housing Explorer — LIHTC, Multifamily, Public Housing + Opportunity Zones
One unified property list across three HUD programs at once, with a designated Qualified Opportunity Zone flag computed by point-in-polygon. Keyless, straight from HUD's own eGIS ArcGIS services.
| Program | What it is | Live count |
|---|---|---|
| LIHTC | Low-Income Housing Tax Credit properties | 50,566 |
| HUD_INSURED | FHA-insured multifamily properties | 17,582 |
| PUBLIC_HOUSING | Public housing developments | 6,211 |
| Opportunity Zones | Treasury-designated QOZ census tracts | 8,765 |
Counts measured live on 2026-08-01 and re-measured on every run — see
data_vintage.
What makes this different
- Three programs, one row shape. LIHTC, FHA-insured multifamily and public housing normally live in three incompatible datasets. Here they share a single schema, so a portfolio screen is one query.
- Nothing is asserted that was not checked.
opportunity_zoneis three-valued:true,false, ornullwhen it could not be tested. Every row names which HUD layers answered (lihtc_status,hud_insured_status,public_housing_status) and whether the result set is complete (result_complete). - Truncation is disclosed, not silent. Every row carries
records_available_in_scope— the exact number of records your filter matched — next torecords_returnedandresults_truncated. - HUD republishes these layers annually. Before anything is billed, the run asserts each layer's name, its column set and its record count against values measured live, plus a positive canary and a negative control on the QOZ layer. If HUD swaps a layer for a different vintage, the run fails loudly instead of quietly answering from stale designations.
Who this is for
- Affordable-housing developers and syndicators — comparable LIHTC deals in a market, with allocation amount, credit type, bond financing, QCT/DDA basis boost and unit mix.
- CRE acquisition teams — FHA-insured multifamily with REAC inspection scores, loan maturity, watch-list and default flags.
- Housing policy and research — subsidised stock per state or city with resident demographics and occupancy.
- Opportunity Zone investors — properties inside designated QOZ tracts, with the tract GEOID and Treasury rural flag.
- AI agents — one call per state; three-valued flags and per-layer status so the agent can tell "no" from "not checked".
Example input
{"states": ["CO"],"programs": ["LIHTC", "HUD_INSURED", "PUBLIC_HOUSING"],"city": "","minUnits": 0,"includeOpportunityZoneFlag": true,"onlyOpportunityZone": false,"maxResults": 500}
city matches as a prefix, case-insensitively. minUnits and
onlyOpportunityZone are applied after fetching, so the run pulls a buffer
beyond maxResults when either is set.
Output — every field
The property
| Field | Meaning |
|---|---|
program | LIHTC, HUD_INSURED, or PUBLIC_HOUSING. |
source_id | HUD's own identifier: LIHTC HUD_ID, FHA property ID, or public-housing development code. |
property_name | Project or property name. |
address, city, state, zip, county | Location as HUD publishes it. |
standardized_address | HUD's USPS-standardized street address. |
census_tract_geoid | 11-digit census tract GEOID from HUD's own geocoding columns. |
msa_name, cbsa_name | Metro area. |
latitude, longitude | WGS84 coordinate, where HUD publishes one. |
units_total | Total units. |
units_low_income | Income-restricted / assisted units. |
units_occupied, units_vacant, occupancy_pct | Occupancy detail. |
unit_mix | Bedroom mix (LIHTC in counts, Public Housing in percentages). |
year_placed_in_service | Year the property entered service. |
owner_or_manager | Owner or management agent. HUD leaves the LIHTC COMPANY column empty for all 50,566 LIHTC properties nationwide, so this is populated on HUD Insured and Public Housing rows only — use contact_name for LIHTC. |
contact_name, contact_phone, contact_email | Published contact for the property. |
program_detail | Free-text program summary. |
Opportunity Zone
| Field | Meaning |
|---|---|
opportunity_zone | Three-valued. true = inside a designated QOZ tract. false = tested against the loaded QOZ layer and outside one. null = NOT tested. |
opportunity_zone_status | Why: ok, unavailable (the layer did not answer), no_polygons_returned, no_coordinate_published (HUD publishes no lat/lon for this property), or not_requested. |
oz_tract_geoid | GEOID10 of the QOZ tract. |
oz_is_rural | Treasury rural flag on that tract. |
LIHTC detail
| Field | Meaning |
|---|---|
year_allocated | Year the tax credits were allocated (distinct from placed-in-service). |
allocation_amount_usd | Annual credit allocation. |
is_bond_financed | Tax-exempt bond financed, i.e. the 4% credit rather than the competitive 9%. |
credit_type | HUD's credit-type code. |
construction_type | New construction, acquisition and rehab, or both. |
nonprofit_sponsor | A nonprofit is the project sponsor. |
target_population | Family, Elderly, Disabled, Homeless. |
in_qualified_census_tract | In a HUD Qualified Census Tract (30% basis boost). |
in_difficult_development_area | In a HUD Difficult Development Area (30% basis boost). |
HUD Insured Multifamily detail
| Field | Meaning |
|---|---|
reac_last_inspection_score | Most recent REAC physical-inspection score, 0-100. Below 60 triggers HUD enforcement. |
reac_last_inspection_date | Date of that inspection. |
has_section_8 | Active Section 8 contract. |
is_subsidized | Carries HUD subsidy. |
is_section_202_811 | Section 202 (elderly) or Section 811 (disabled). |
is_in_default | The FHA-insured mortgage is in default or delinquent. |
on_watch_list | HUD has placed the property on its watch list. |
loan_maturity_date | Maturity of the insured mortgage. |
original_loan_amount_usd | Original insured mortgage amount. |
contract_expiration_date | When the primary HUD assistance contract expires — the date the affordability restriction can lapse. Populated on 4,903 of 17,582 properties. |
Public Housing detail
| Field | Meaning |
|---|---|
pha_code | Public Housing Authority participant code. |
pha_total_units | Total units that PHA operates. |
scattered_site | A scattered-site development rather than a single building. |
Residents
| Field | Meaning |
|---|---|
pct_elderly | Residents aged 62+. |
pct_disabled | Residents reported with a disability. |
pct_minority | Residents reported as a racial or ethnic minority. |
avg_household_income_usd | Average annual household income. |
avg_rent_per_month_usd | Average monthly tenant-paid rent. |
Provenance — read these before trusting a result set
| Field | Meaning |
|---|---|
lihtc_status, hud_insured_status, public_housing_status | Per-layer outcome: ok, partial, unavailable, or null (not requested). |
programs_queried | Every program this run should have consulted. |
programs_ok | The ones that answered. |
programs_failed | The ones that did not. null when all answered. |
result_complete | true only when every requested layer answered. |
records_available_in_scope | Exact count matching your state/city filter, before minUnits / onlyOpportunityZone and before maxResults. |
records_returned | Rows actually returned. |
results_truncated | true when fewer rows came back than matched the scope. |
data_vintage | The record and column counts measured on each HUD layer at run time. A change between runs means HUD republished a layer. |
checked_at | UTC ISO-8601 timestamp of the run. |
Inputs that populate the sparser columns
| Columns | Verified live input |
|---|---|
contract_expiration_date | {"states":["NY"],"programs":["HUD_INSURED"],"maxResults":500} — HUD publishes it on 4,903 of 17,582 properties (281 in NY, 47 in CO). |
reac_last_inspection_score, loan_maturity_date, has_section_8, contact_email | Any run including HUD_INSURED. |
in_qualified_census_tract, allocation_amount_usd, unit_mix | Any run including LIHTC. |
pha_code, units_occupied, scattered_site | Any run including PUBLIC_HOUSING. |
programs_failed | Populated only when a HUD layer fails. null on a healthy run is the good outcome. |
How it works
- Live drift assertions run first, before anything billable. Each layer's name, column set and record count is checked against values measured on 2026-08-01, and the QOZ layer additionally gets a positive canary and a negative control (a state code that must match nothing — this catches a silently-ignored filter). Any failure fails the run and bills nothing.
- Each program is queried separately with an exact
returnCountOnlycount so truncation can be disclosed, then paged. - The program groups are round-robin merged so a capped run stays representative of every program rather than being monopolised by LIHTC, which has the most rows.
- Designated QOZ tract polygons for the selected states are loaded and each
property is tested by point-in-polygon. If the layer did not answer, or HUD
publishes no coordinate for a property,
opportunity_zoneisnull. - Filters are applied, results capped, and the billing gate runs before anything is written. If every layer failed, or nothing matched, the run fails with a message naming the cause.
Use from an AI agent: quickstart
The smallest input that returns a real answer. Verified live on 2026-10-05: the run SUCCEEDED and returned 5 rows in about 10 seconds.
{"states": ["CO"],"maxResults": 5}
Call it through the hosted MCP server (https://mcp.apify.com?tools=malonestar/hud-affordable-housing-explorer) or straight over HTTP:
POST https://api.apify.com/v2/acts/malonestar~hud-affordable-housing-explorer/run-sync-get-dataset-itemsAuthorization: Bearer YOUR_APIFY_TOKENContent-Type: application/json
with the JSON above as the body. The response is a JSON array of dataset rows.
What comes back: one row per assisted property. Abbreviated first row from that run (the full row has 69 fields, all described in the output schema):
{"program": "LIHTC","property_name": "MARYEL MANOR","address": "12555 SHERIDAN","city": "BROOMFIELD","state": "CO","units_total": 50,"units_low_income": 50,"opportunity_zone": false,"result_complete": true,"programs_failed": null}
Read before you conclude anything: result_complete, programs_failed and results_truncated. opportunity_zone: null means unknown, not "not in a zone".
Input notes for agents
statesis required: 2-letter codes (["TX"]) or full state names. There is no default state; an omitted, empty or unrecognised value fails the run and bills nothing (before build 1.1.5 it silently searched Colorado).programsis any ofLIHTC,HUD_INSURED,PUBLIC_HOUSING;citynarrows within a state.maxResultsdefaults to 1,000.
Cost: $0.0060 per row at the Free-tier list price (paid plans get a discount), so the example above costs about $0.030. A run that cannot answer fails and bills no rows.
Use as an MCP tool
Available through mcp.apify.com. One call per state. An agent should treat
opportunity_zone: null as unknown (never as "not in an Opportunity Zone"),
and check result_complete before concluding a market has no assisted housing.
Pricing
Pay-per-result: one property row = one billed result. A failed run emits nothing and costs nothing beyond the platform's Actor-start event. Volume tiers apply automatically on paid Apify plans.
Was this useful?
If a run answered your question, a short review on this Store page helps other buyers find the Actor; every published review is read. If a run gave a wrong or confusing answer, open an Issue on this page with the run ID and it will be treated as a defect, not feedback.
FAQ
Why is owner_or_manager empty on LIHTC rows? Because HUD leaves the
COMPANY column empty for every one of the 50,566 LIHTC properties it
publishes — verified live. contact_name and contact_phone carry the LIHTC
allocation contact instead.
Why is opportunity_zone null on some rows? Because the property could not
be tested — usually because HUD publishes no coordinate for it. Read
opportunity_zone_status. A null is not a negative finding.
Why did the run fail instead of returning zero rows? Because a zero that comes from a failed layer, an over-tight filter or an outage is indistinguishable from a genuine "no such housing here". The failure message names the cause and nothing is billed.
How do I get everything in a state? Raise maxResults. Compare
records_returned with records_available_in_scope to see what you are
missing.
Is the Opportunity Zone flag legal advice? No. It is a point-in-polygon screen against HUD's published QOZ tract layer. Confirm with the tract's official designation before relying on it.
Related actors
- hud-qct-lihtc-boost-screener — Qualified Census Tract / DDA basis-boost check for an arbitrary coordinate.
- nyc-landlord-registry-lead-list, distressed-property-signal-stacker — owner contact and distress signals for NYC multifamily.
- fdic-sod-deposit-market-share-rollup — the lending market around a metro.
Telemetry
This actor can send one usage record per run to the publisher: the channel the run was started from (Console, API, MCP, scheduler, ...), a timestamp, the build number, a salted one-way hash of the account id (so repeat use can be counted without storing the id itself), and the NAMES of the input fields used. It never records input values, results or the account id, and it is used only to see how buyers reach the actor. It cannot slow the run by more than 3 seconds and can never affect your results or your bill.