HUD QCT LIHTC Boost Screener — 130% Basis by Coordinate
Pricing
from $3.30 / 1,000 results
HUD QCT LIHTC Boost Screener — 130% Basis by Coordinate
Qualified Census Tract (QCT) lookup for any coordinate — instantly flag the LIHTC 130% eligible-basis boost per site. Batch lat/lon points against HUD's official QCT layer; each returns in_qct, the 11-digit tract GEOID, state, county FIPS, tract, QCT vintage, and boost-eligible flag. MCP-ready.
Pricing
from $3.30 / 1,000 results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
11 days ago
Last modified
Categories
Share
HUD QCT + DDA Screener - LIHTC 130% Basis Boost
Batch-screen coordinates against the two HUD designations that grant the 130% eligible-basis boost for Low-Income Housing Tax Credit deals under IRC Sec. 42(d)(5)(B): a Qualified Census Tract (QCT) and a Difficult Development Area (DDA). One clean row per site, with the designation vintage stamped on every row and a year-over-year comparison so you can see when a tract has been added or dropped.
Keyless. Three HUD.Official.Content ArcGIS services, queried live on every run:
| Source | Layer | Live count (2026-08-01) |
|---|---|---|
| QCT, current year | QUALIFIED_CENSUS_TRACTS_2026 | 14,496 |
| QCT, prior year | QUALIFIED_CENSUS_TRACTS_2025 | 15,727 |
| DDA, current year | Difficult_Development_Areas2026 | 2,902 |
What changed in v1.1 (2026-08-01), and why it matters to a tax position
v1.1 is a hardening release. Two of the changes fix answers that were materially wrong, both reproduced live. No output field was removed or renamed and the price is unchanged.
1. v1.0 was screening a superseded designation
v1.0 queried only HUD's 2025 QCT service while HUD publishes a 2026 one, and it
stamped a hard-coded qct_year: "FY2025" constant that could never notice a new
year had been published. QCT designations churn materially - Colorado went from 258
QCTs in 2025 to 224 in 2026: 49 tracts dropped, 15 added.
Worked example, in the prefill: Grand Junction, CO tract 08077000200
(39.07095, -108.55684) is a QCT in 2025 and not a QCT in 2026. v1.0 reported
lihtc_130_basis_boost_eligible: true. v1.1 reports false, with
in_qct_prior_year: true and qct_designation_change: "dropped_since_prior_year" so
the prior-year answer is still there if you need it.
qct_year, qct_prior_year and dda_year are now derived at runtime from the live
layer names, and a layer name that no longer parses fails the run rather than
guessing.
2. v1.0 asserted "no boost" without ever checking DDA
lihtc_130_basis_boost_eligible mirrored in_qct alone. But the same 130% boost
applies inside a Difficult Development Area, and the old code's claim that DDA "is not
a point-queryable FeatureServer" is simply wrong - HUD publishes it as polygons.
Worked example, and it was the Actor's own control coordinate: Beverly Hills, CA
(34.073, -118.400) sits inside the 2026 Small-Area DDA "Los Angeles-Long
Beach-Glendale, CA HUD Metro FMR Area", ZCTA 90210. v1.0 reported
lihtc_130_basis_boost_eligible: false. v1.1 reports true with
basis_boost_basis: "dda".
3. A failed lookup can no longer read as "not eligible"
v1.0 initialised in_qct and lihtc_130_basis_boost_eligible to false in the base
row. Every derived flag now starts null: null means not checked, false means
checked and negative. Each row carries qct_status, qct_prior_status and
dda_status, the boost flag is gated so it can never read false while a contributing
source is unavailable, and if every source fails for every asset the run fails and
bills nothing. The billing gate runs before any row is pushed.
4. Live drift assertions run before any billable row
Both QCT layers store Web Mercator, so a dropped inSR=4326 makes the service answer
HTTP 200 with zero features - a false clear. Every run now proves: the layer names
parse to a vintage and the "current" layer is not older than the "prior" one; national
feature counts sit inside a measured band; DDA_TYPE contains only the closed
vocabulary SA / NM; a positive QCT canary (South Bronx NY must resolve to tract
36005007900); a positive DDA canary (Beverly Hills must resolve to a Los Angeles DDA);
and a negative control (an open-Pacific point must return exactly 0 from all three
layers).
Who this is for
- LIHTC developers and syndicators pre-qualifying a pipeline of sites for the 130% eligible-basis boost before ordering an appraisal or a market study.
- Affordable-housing consultants and tax counsel who need the vintage on the record, because allocation and placed-in-service timing determine which year's designation applies.
- State housing finance agencies and CDFIs batch-scoring applications.
- Real-estate and site-selection teams layering the boost onto other constraints.
- AI agents doing site due diligence - see the MCP note below.
Example input
{"assets": [{ "lat": 40.815, "lon": -73.904, "label": "South Bronx, NY (Qualified Census Tract)" },{ "lat": 34.073, "lon": -118.4, "label": "Beverly Hills, CA (Small Area DDA - boost via DDA, not QCT)" },{ "lat": 39.07095, "lon": -108.55684, "label": "Grand Junction, CO (QCT in the prior year, not the current one)" },{ "lat": 39.0, "lon": -101.5, "label": "Rural Kansas (genuine clear - neither QCT nor DDA)" }],"maxAssets": 500}
That is the prefilled input. Live on build 1.1.2 it returns four rows:
| label | boost eligible | basis | in_qct (FY2026) | prior (FY2025) | change | DDA |
|---|---|---|---|---|---|---|
| South Bronx, NY | true | qct | true (36005007900) | true | qct_in_both_years | - |
| Beverly Hills, CA | true | dda | false | false | not_a_qct_in_either_year | Los Angeles-Long Beach-Glendale, CA HUD Metro FMR Area (SA, ZCTA 90210) |
| Grand Junction, CO | false | neither_qct_nor_dda | false | true (08077000200) | dropped_since_prior_year | - |
| Rural Kansas | false | neither_qct_nor_dda | false | false | not_a_qct_in_either_year | - |
Rows two and three are exactly the answers v1.0 got wrong.
Input fields
| Field | Type | Required | Description |
|---|---|---|---|
assets | array | yes | Points to screen. Each item: lat, lon (WGS84 decimal degrees), optional label. One billable row per asset. |
maxAssets | integer | no | Safety cap on how many assets are screened. Default 500, max 5,000. |
Omitting assets returns HTTP 400 at run creation - the run never starts and
nothing is billed.
Output fields
One row per screened asset. Every one of the 45 declared fields is listed here.
null on a flag always means the backing source did not answer, never the answer is no.
| Field | Type | Description |
|---|---|---|
asset_label | string | null |
asset_lat | number | null |
asset_lon | number | null |
in_qct | boolean | null |
tract_geoid | string | null |
state | string | null |
county_fips | string | null |
tract | string | null |
qct_year | string | null |
lihtc_130_basis_boost_eligible | boolean | null |
error | string | null |
source_url | string | null |
checked_at | string | null |
qct_status | string | null |
qct_error | string | null |
qct_prior_status | string | null |
qct_prior_error | string | null |
dda_status | string | null |
dda_error | string | null |
sources_ok_count | integer | null |
sources_total_count | integer | null |
partial_result | boolean | null |
qct_layer_name | string | null |
qct_prior_year | string | null |
qct_prior_layer_name | string | null |
dda_year | string | null |
dda_layer_name | string | null |
in_qct_prior_year | boolean | null |
tract_geoid_prior_year | string | null |
qct_designation_change | string | null |
state_fips | string | null |
tract_name | string | null |
in_dda | boolean | null |
dda_count | integer | null |
dda_type | string | null |
dda_type_label | string | null |
dda_name | string | null |
dda_code | string | null |
dda_zcta5 | string | null |
basis_boost_basis | string | null |
basis_boost_statutory_citation | string | null |
disclaimer | string | null |
source_url_qct_prior | string | null |
source_url_dda | string | null |
actor_version | string | null |
Fields that are legitimately null on a healthy run
A dead-column audit over the FULL dataset of a live run with the schema prefill
(build 1.1.2) leaves exactly four fields null: qct_error, qct_prior_error,
dda_error and error. All four being null is good news - it means every source
answered. Each is pinned by a fault-injection fixture in offline.test.js, and each
populates on any run during an upstream outage.
dda_* detail fields are null when the site is not in a DDA, and tract_geoid is null
when it is not in a current-year QCT; the prefill deliberately includes one of each so
every column populates on an auto-QA run.
Use as an MCP tool
This Actor is available to AI agents through mcp.apify.com. It is a good chainable
tool because it takes plain coordinates and returns a flat, self-describing row:
lihtc_130_basis_boost_eligible for the yes/no, basis_boost_basis for which
designation carried it, qct_year / dda_year for the vintage, and qct_status /
dda_status so an agent can tell a verified negative from an unanswered one.
Pricing
$6 per 1,000 results (pay-per-event, one Result event per screened asset). Graduated discounts apply on paid Apify plans. There are no other charge events.
Billing honesty: if every asset fails against every source the run fails and emits nothing, so you are not billed for an outage.
FAQ
Does lihtc_130_basis_boost_eligible: false mean there is definitely no boost?
No. It means the site is in neither a current-year QCT nor a current-year DDA. State
allocating agencies may still grant a discretionary basis boost under
IRC Sec. 42(d)(5)(B)(v) for buildings that need it for financial feasibility, and that
is a QAP decision this screen cannot see.
Which year applies to my deal? That depends on allocation and placed-in-service
timing, and it is a question for your tax counsel. This Actor gives you both years -
in_qct with qct_year, and in_qct_prior_year with qct_prior_year - plus
qct_designation_change so you can see immediately when a site sits on the boundary
between them.
Why does a site show in_qct: false but in_qct_prior_year: true? Because HUD
re-designates QCTs annually against updated income and poverty data. Colorado lost 49
tracts and gained 15 between 2025 and 2026. qct_designation_change reads
dropped_since_prior_year.
What is the difference between an SA and an NM DDA? SA is a Small-Area
(metropolitan) DDA, designated by ZIP Code Tabulation Area - dda_zcta5 is populated.
NM is a Non-Metropolitan DDA, designated by county - dda_zcta5 is null there, which
is the source's own convention and not a missing value.
Why did my run fail with "LIVE DRIFT CHECK FAILED"? Because HUD's services no
longer match what this Actor was verified against - a renamed or re-vintaged layer, a
new DDA_TYPE code, or a canary that stopped returning known-good data. A loud failure
is the correct answer: the alternative is a plausible tax screen computed from a source
that has silently changed. Nothing is billed on a failed run.
Is this tax advice? No. It is an informational screen against public HUD data.
Confirm against HUD's official QCT/DDA datasets at
huduser.gov/portal/datasets/qct.html and with your tax-credit counsel.
Related Actors
- HUD Affordable Housing Explorer - LIHTC, multifamily and public-housing inventory, plus Opportunity Zone point-in-polygon.
- CDFI NMTC Low-Income & Energy Bonus Screener - the NMTC and IRA energy-community bonus-credit designations for the same coordinate.
- FEMA NRI County Risk Profile and Site Due Diligence Bundle - the physical constraints that sit alongside the financial ones.
Informational screening tool built on public HUD data. Not tax advice, and not a HUD
or IRS determination. Designations are re-published annually - always check the
qct_year and dda_year on the row. Confirm with HUD and your tax-credit counsel
before relying on the 130% basis boost.