HUD QCT LIHTC Boost Screener — 130% Basis by Coordinate avatar

HUD QCT LIHTC Boost Screener — 130% Basis by Coordinate

Pricing

from $3.30 / 1,000 results

Go to Apify Store
HUD QCT LIHTC Boost Screener — 130% Basis by Coordinate

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

Kyle Maloney

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

4 hours ago

Last modified

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:

SourceLive layer nameDesignationLive count (2026-10-24)
QCT, currentQualified_Census_TractsFY202714,539
QCT, earlier vintageQUALIFIED_CENSUS_TRACTS_2025FY202515,727
DDA, currentDifficult_Development_AreasFY20273,000

The two "current" service URLs still contain _2026. HUD overwrote them with the FY2027 designations on 2026-10-23 and stripped the year from the layer names, so neither the URL nor the layer name states the vintage any more — see §1b.


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 established at runtime, never from a constant, and a vintage that cannot be established fails the run rather than guessing.

1b. v1.2 — HUD now republishes these services in place, so no URL or layer name is evidence of the vintage

On 2026-10-23 HUD published the FY2027 designations (effective 2027-01-01) by overwriting the FY2026 feature services and stripping the year from both layer names:

beforeafter
QCT layerQUALIFIED_CENSUS_TRACTS_2026Qualified_Census_Tracts
DDA layerDifficult_Development_Areas2026Difficult_Development_Areas

The service URLs still say _2026, the ArcGIS item titles still say 2026, and the item description still says 2025 — all three are now stale. Only the data moved. Taking the year from the URL would stamp FY2026 on FY2027 answers, which is the v1.0 defect re-committed behind a passing drift check.

So v1.2 identifies the vintage from the data: the designated tract set is the designation, and a name can be renamed. The live counts are matched against HUD's own published designation files (huduser.gov/portal/datasets/qct/QCT<YYYY>CSV.zip), measured 2026-10-24:

vintagetractsCONYTX
FY202515,7272581,0431,401
FY202614,4962241,0261,326
FY202714,5392171,0391,341

The live "_2026" service returns 14,539 / 217 / 1,039 / 1,341 — FY2027, exactly — and its Colorado GEOID set matches HUD's FY2027 list 217 of 217 with zero difference in either direction. Every row now carries qct_year_basis / dda_year_basis saying how the year was established: layer_name (HUD published it — strongest), data_fingerprint (HUD stripped it, so the designated-feature counts identified it), or layer_name_and_data_fingerprint (both, in agreement). If the two disagree, or if the fingerprint matches no known vintage, the run fails — so when FY2028 lands a human updates the table deliberately instead of a guessed year shipping on a tax screen.

One consequence worth knowing: the FY2026 designation is no longer published keyless anywhere. HUD overwrote it, so the earlier-vintage comparison is now FY2025 and spans two years. qct_designation_change says "this year" / "prior year", so it is emitted only when the two vintages are consecutive; across a gap it is null and qct_designation_change_note says why, with qct_comparison_years naming the two vintages actually compared. in_qct and in_qct_prior_year remain true statements about their own stated vintage, and the boost verdict never depended on the comparison.

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 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: each live layer name is still recognisably the right layer; the designation vintage can be established from the layer name or the designated-feature fingerprint, and the two agree where both are available; the "current" layer is not older than the earlier 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. Measured against live HUD on 2026-10-24 it returns four rows, with qct_year: FY2027 (basis data_fingerprint) and qct_prior_year: FY2025:

labelboost eligiblebasisin_qct (FY2027)earlier (FY2025)changeDDA
South Bronx, NYtrueqcttrue (36005007900)true (36005007900)null (2-year span)-
Beverly Hills, CAtrueddafalsefalsenull (2-year span)Los Angeles-Long Beach-Glendale, CA HUD Metro FMR Area (SA, ZCTA 90210)
Grand Junction, COfalseneither_qct_nor_ddafalsetrue (08077000200)null (2-year span)-
Rural Kansasfalseneither_qct_nor_ddafalsefalsenull (2-year span)-

Rows two and three are exactly the answers v1.0 got wrong. qct_designation_change is null on all four because HUD overwrote the FY2026 service with FY2027, so the only earlier vintage it still publishes keyless is FY2025 — two years back, not one. Each row carries qct_comparison_years: "FY2027 vs FY2025" and a qct_designation_change_note saying so. The boost verdict is unaffected: it never depended on the comparison.

Input fields

FieldTypeRequiredDescription
assetsarrayyesPoints to screen. Each item: lat, lon (WGS84 decimal degrees), optional label. One billable row per asset.
maxAssetsintegernoSafety 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.

FieldTypeDescription
asset_labelstringnull
asset_latnumbernull
asset_lonnumbernull
in_qctbooleannull
tract_geoidstringnull
statestringnull
county_fipsstringnull
tractstringnull
qct_yearstringnull
qct_year_basisstringnull
lihtc_130_basis_boost_eligiblebooleannull
errorstringnull
source_urlstringnull
checked_atstringnull
qct_statusstringnull
qct_errorstringnull
qct_prior_statusstringnull
qct_prior_errorstringnull
dda_statusstringnull
dda_errorstringnull
sources_ok_countintegernull
sources_total_countintegernull
partial_resultbooleannull
qct_layer_namestringnull
qct_prior_yearstringnull
qct_prior_year_basisstringnull
qct_prior_layer_namestringnull
dda_yearstringnull
dda_year_basisstringnull
dda_layer_namestringnull
in_qct_prior_yearbooleannull
tract_geoid_prior_yearstringnull
qct_designation_changestringnull
qct_comparison_yearsstringnull
qct_designation_change_notestringnull
state_fipsstringnull
tract_namestringnull
in_ddabooleannull
dda_countintegernull
dda_typestringnull
dda_type_labelstringnull
dda_namestringnull
dda_codestringnull
dda_zcta5stringnull
basis_boost_basisstringnull
basis_boost_statutory_citationstringnull
disclaimerstringnull
source_url_qct_priorstringnull
source_url_ddastringnull
actor_versionstringnull

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 (measured 2026-10-24) leaves exactly five fields null: qct_error, qct_prior_error, dda_error, error and qct_designation_change. The first four being null is good news - it means every source answered; each is pinned by a fault-injection fixture in offline.test.js and populates on any run during an upstream outage. qct_designation_change is null for the structural reason above (HUD's two keyless QCT vintages are currently two years apart, so the adjacent-year vocabulary would be a false claim); qct_designation_change_note carries the reason on every row, and the field populates again the moment HUD publishes two consecutive vintages.

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 from an AI agent: quickstart

The smallest input that returns a real answer. Verified live on 2026-10-05: the run SUCCEEDED and returned 1 row in about 25 seconds.

{
"assets": [
{
"lat": 40.815,
"lon": -73.904,
"label": "South Bronx NY"
}
]
}

Call it through the hosted MCP server (https://mcp.apify.com?tools=malonestar/hud-qct-lihtc-boost-screener) or straight over HTTP:

POST https://api.apify.com/v2/acts/malonestar~hud-qct-lihtc-boost-screener/run-sync-get-dataset-items
Authorization: Bearer YOUR_APIFY_TOKEN
Content-Type: application/json

with the JSON above as the body. The response is a JSON array of dataset rows.

What comes back: exactly one row per site. Abbreviated first row from that run (the full row has 50 fields, all described in the output schema):

{
"asset_label": "South Bronx NY",
"in_qct": true,
"tract_geoid": "36005007900",
"qct_year": "FY2027",
"qct_year_basis": "data_fingerprint",
"in_dda": false,
"dda_year": "FY2027",
"lihtc_130_basis_boost_eligible": true,
"basis_boost_basis": "qct",
"qct_status": "ok",
"dda_status": "ok"
}

Read before you conclude anything: qct_status and dda_status (ok means verified; anything else means unanswered, and the eligibility field is then not a negative). And read qct_year / dda_year — the designation changes every year and the service URL does not tell you which one you got.

Input notes for agents

  • assets is an array of { "lat": number, "lon": number, "label": "optional" }.
  • The boost can come from a QCT or a DDA; basis_boost_basis says which.

Cost: $0.0060 per row at the Free-tier list price (paid plans get a discount), so the example above costs about $0.006. A run that cannot answer fails and bills no rows.

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 vintages it can reach - in_qct with qct_year, and in_qct_prior_year with qct_prior_year - plus qct_comparison_years naming the pair, and qct_designation_change when the two are consecutive years.

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 went 258 QCTs in 2025 -> 224 in 2026 -> 217 in 2027.

Why is qct_designation_change null right now? Because HUD published FY2027 by overwriting its FY2026 service on 2026-10-23, so the only earlier vintage it still publishes keyless is FY2025. That is a two-year span, and this field's vocabulary says "this year" / "prior year" - reporting dropped_since_prior_year across the gap would assert a change in FY2026, a year the run never looked at. qct_designation_change_note says so on every row, and the field starts reporting again as soon as HUD publishes two consecutive vintages. If you need the FY2026 designation, HUD still publishes it as a file at huduser.gov/portal/datasets/qct/QCT2026CSV.zip.

Can I trust qct_year? It is established from the data, not from the URL or the layer name, and qct_year_basis tells you how. HUD republishes these services in place and has stripped the year from the layer names, so a URL containing _2026 is not evidence of anything. If the vintage cannot be established, or the layer name and the data disagree about it, the run fails rather than emitting a guessed 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.

  • 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.