transcript.md
↑ ContentsState Planner chat · https://stateplanner.com
- State: Missouri (MO)
- Started: October 26, 2026, 3:40 PM UTC
- Runtime: 40 sec
- Status: completed
Prompt
Missouri Building Permits by County: New Housing Units 2020-2025
Model: Anthropic: Claude Opus 5.5 (anthropic/claude-opus-5.5, via openrouter)
Response
Now the map and a yearly chart.
Missouri building permits by county: new housing units, 2020–2025
From 2020 through 2025, building permits authorized about 114,800 new privately owned housing units in Missouri. These figures come from the Census Bureau's Building Permits Survey, using its estimates with imputation. The yearly total peaked at 21,372 units in 2021, fell to 16,781 in 2023, and was 17,474 in 2025. Single-family homes made up about 63% of all units. Buildings with 5 or more units peaked at about 8,100 units in 2022.


Map legend: Each county is shaded by the total units permitted from 2020 to 2025: dark purple for 5,000 or more, then magenta for 2,500–4,999, pink for 1,000–2,499, light pink for 250–999, pale for 1–249, white for 0, and gray for no data. Click a county to see its building-size breakdown and its units per 1,000 residents, based on the 2025 population estimate.
Top 20 counties by new housing units permitted, 2020–2025
| County | Units | Single-family | 5+ unit bldgs | Per 1,000 residents |
|---|---|---|---|---|
| Jackson | 23,387 | 10,027 | 11,487 | 31.9 |
| St. Charles | 14,972 | 8,944 | 5,685 | 35.1 |
| St. Louis County | 7,346 | 4,827 | 2,488 | 7.4 |
| Greene | 7,309 | 5,181 | 1,873 | 23.6 |
| Boone | 5,439 | 4,178 | 1,191 | 28.4 |
| Cass | 5,293 | 3,357 | 1,382 | 45.7 |
| Jasper | 4,665 | 3,035 | 1,042 | 36.6 |
| Jefferson | 4,550 | 4,255 | 200 | 19.5 |
| Christian | 3,924 | 3,384 | 307 | 40.6 |
| Franklin | 3,686 | 2,651 | 911 | 34.1 |
| St. Louis city | 3,397 | 556 | 2,767 | 12.2 |
| Clay | 2,874 | 1,606 | 1,206 | 10.8 |
| Taney | 2,662 | 2,211 | 350 | 46.7 |
| Warren | 2,217 | 1,602 | 491 | 56.8 |
| Camden | 2,118 | 1,516 | 461 | 48.1 |
| Platte | 2,046 | 1,593 | 299 | 17.9 |
| Stone | 1,492 | 1,409 | 35 | 45.9 |
| St. Francois | 1,168 | 468 | 538 | 17.2 |
| Lincoln | 1,102 | 751 | 228 | 16.5 |
| Cole | 1,011 | 938 | 0 | 13.0 |
Findings
- Jackson County had about 20% of the state's permitted units, and nearly half of its units were in buildings of 5 or more units. Together with St. Charles County, it accounts for a third of the statewide total.
- Per resident, the highest rates were in Warren (56.8 per 1,000), Camden (48.1), Taney (46.7), Stone (45.9) and Cass (45.7). St. Louis County ranks third by total units but has one of the lowest rates (7.4 per 1,000).
- In St. Louis city, most units were in 5+ unit buildings (2,767 of 3,397).
Notes
- Missing or zero counties: Hickory County has no permit rows in 2020–2025, and Wayne County has rows for only 3 of the 6 years. Dade, Putnam, Reynolds, Shannon, Sullivan and Wayne report zero units. This usually means few or no places in those counties issue permits, not that nothing was built.
- What's not counted: The survey covers only new private housing. Manufactured homes, conversions and construction in areas without permit requirements are not included.
- Permits aren't completions: A permit authorizes construction. It is not a start or a completion, and some permitted units are never built.
- Population basis: The per-1,000 rates divide six years of permits by the 2025 Census population estimate.
I can also break this down by city or permit office, show construction value, or compare these years with the mid-2000s peak.
Chart Queries
Missouri Housing Units Permitted by Building Size, 2020-2025
SELECT YEAR AS "Year",
s AS "Building size",
u AS "Units"
FROM (
SELECT YEAR,
sum(units_1) a,
sum(units_2 + units_3_4) b,
sum(units_5plus) c
FROM mo_bps_county_permits
WHERE YEAR BETWEEN 2020 AND 2025
GROUP BY YEAR
) t
CROSS JOIN LATERAL (
VALUES ('1 unit', a),
('2-4 units', b),
('5+ units', c)
) v(s, u)
ORDER BY 1
charts/turn-1-mo-units-by-year.csv
Picture: charts/turn-1-mo-units-by-year.png
Exploratory Queries
Query 1 — Check year coverage and statewide totals 2020-2025.
SELECT YEAR,
count(*) n,
sum(units_total) units,
sum(units_1) sf,
sum(units_2 + units_3_4) small_mf,
sum(units_5plus) mf5
FROM mo_bps_county_permits
WHERE YEAR >= 2020
GROUP BY YEAR
ORDER BY YEAR
| year | n | units | sf | small_mf | mf5 |
|---|---|---|---|---|---|
| 2020 | 114 | 19839 | 13107 | 1064 | 5668 |
| 2021 | 114 | 21372 | 13941 | 1073 | 6358 |
| 2022 | 114 | 20901 | 11598 | 1178 | 8125 |
| 2023 | 113 | 16781 | 11022 | 1209 | 4550 |
| 2024 | 113 | 18464 | 11528 | 1599 | 5337 |
| 2025 | 113 | 17474 | 11374 | 1334 | 4766 |
Query 2 — Rank counties by total units 2020-2025, with mix and rate per 1,000 residents.
WITH p AS (
SELECT geoid,
sum(units_total) u,
sum(units_1) sf,
sum(units_5plus) mf5,
count(*) yrs
FROM mo_bps_county_permits
WHERE YEAR BETWEEN 2020 AND 2025
GROUP BY geoid
),
pop AS (
SELECT geoid,
pop_estimate
FROM mo_census_county_pop_est
WHERE YEAR = (
SELECT max(YEAR)
FROM mo_census_county_pop_est
)
)
SELECT c.name,
p.*,
pop.pop_estimate,
round(1000.0 * p.u / pop.pop_estimate, 1) per1k,
(
SELECT max(YEAR)
FROM mo_census_county_pop_est
) popyr
FROM p
LEFT JOIN mo_census_counties c USING (geoid)
LEFT JOIN pop USING (geoid)
ORDER BY u DESC
LIMIT 20
| name | geoid | u | sf | mf5 | yrs | pop_estimate | per1k | popyr |
|---|---|---|---|---|---|---|---|---|
| Jackson County | 29095 | 23387 | 10027 | 11487 | 6 | 732994 | 31.9 | 2025 |
| St. Charles County | 29183 | 14972 | 8944 | 5685 | 6 | 426499 | 35.1 | 2025 |
| St. Louis County | 29189 | 7346 | 4827 | 2488 | 6 | 990911 | 7.4 | 2025 |
| Greene County | 29077 | 7309 | 5181 | 1873 | 6 | 309286 | 23.6 | 2025 |
| Boone County | 29019 | 5439 | 4178 | 1191 | 6 | 191746 | 28.4 | 2025 |
| Cass County | 29037 | 5293 | 3357 | 1382 | 6 | 115859 | 45.7 | 2025 |
| Jasper County | 29097 | 4665 | 3035 | 1042 | 6 | 127428 | 36.6 | 2025 |
| Jefferson County | 29099 | 4550 | 4255 | 200 | 6 | 233132 | 19.5 | 2025 |
| Christian County | 29043 | 3924 | 3384 | 307 | 6 | 96725 | 40.6 | 2025 |
| Franklin County | 29071 | 3686 | 2651 | 911 | 6 | 108090 | 34.1 | 2025 |
| St. Louis city | 29510 | 3397 | 556 | 2767 | 6 | 278144 | 12.2 | 2025 |
| Clay County | 29047 | 2874 | 1606 | 1206 | 6 | 265032 | 10.8 | 2025 |
| Taney County | 29213 | 2662 | 2211 | 350 | 6 | 57001 | 46.7 | 2025 |
| Warren County | 29219 | 2217 | 1602 | 491 | 6 | 39016 | 56.8 | 2025 |
| Camden County | 29029 | 2118 | 1516 | 461 | 6 | 44047 | 48.1 | 2025 |
| Platte County | 29165 | 2046 | 1593 | 299 | 6 | 114400 | 17.9 | 2025 |
| Stone County | 29209 | 1492 | 1409 | 35 | 6 | 32503 | 45.9 | 2025 |
| St. Francois County | 29187 | 1168 | 468 | 538 | 6 | 67809 | 17.2 | 2025 |
| Lincoln County | 29113 | 1102 | 751 | 228 | 6 | 66676 | 16.5 | 2025 |
| Cole County | 29051 | 1011 | 938 | 0 | 6 | 77908 | 13.0 | 2025 |
Query 3 — Find counties with missing years or no permits listed.
SELECT c.name,
c.geoid,
count(p.year) yrs,
coalesce(sum(p.units_total), 0) u
FROM mo_census_counties c
LEFT JOIN mo_bps_county_permits p ON p.geoid = c.geoid
AND p.year BETWEEN 2020 AND 2025
GROUP BY 1,
2
HAVING count(p.year) < 6
OR coalesce(sum(p.units_total), 0) = 0
| name | geoid | yrs | u |
|---|---|---|---|
| Wayne County | 29223 | 3 | 0 |
| Dade County | 29057 | 6 | 0 |
| Hickory County | 29085 | 0 | 0 |
| Reynolds County | 29179 | 6 | 0 |
| Putnam County | 29171 | 6 | 0 |
| Sullivan County | 29211 | 6 | 0 |
| Shannon County | 29203 | 6 | 0 |
Map Layer Queries
Units permitted 2020-2025 (all sizes) — colored by "cls": 5,000+ (#7a0177), 2,500-4,999 (#c51b8a), 1,000-2,499 (#f768a1), 250-999 (#fbb4b9), 1-249 (#feebe2), 0 (#ffffff), No data (#bdbdbd)
WITH p AS (
SELECT geoid,
sum(units_total) u,
sum(units_1) sf,
sum(units_2 + units_3_4) sm,
sum(units_5plus) mf5,
count(*) yrs
FROM mo_bps_county_permits
WHERE YEAR BETWEEN 2020 AND 2025
GROUP BY geoid
),
pop AS (
SELECT geoid,
pop_estimate
FROM mo_census_county_pop_est
WHERE YEAR = 2025
)
SELECT c.geom,
c.name AS "County",
coalesce(p.u, 0) AS "Units 2020-2025",
p.sf AS "Single-family units",
p.sm AS "2-4 unit bldg units",
p.mf5 AS "5+ unit bldg units",
round(1000.0 * coalesce(p.u, 0) / pop.pop_estimate, 1) AS "Units per 1,000 residents(2025 pop)",
coalesce(p.yrs, 0) AS "Years reported",
CASE
WHEN p.yrs IS NULL THEN 'No data'
WHEN p.u = 0 THEN '0'
WHEN p.u < 250 THEN '1-249'
WHEN p.u < 1000 THEN '250-999'
WHEN p.u < 2500 THEN '1,000-2,499'
WHEN p.u < 5000 THEN '2,500-4,999'
ELSE '5,000+'
END AS cls
FROM mo_census_counties c
LEFT JOIN p USING (geoid)
LEFT JOIN pop USING (geoid)
Picture: maps/turn-1-county-permits-2020-2025.png
Used Data Sets
mo_bps_county_permits— last synced 2026-10-14 · 3,993 rowsmo_census_counties— last synced 2026-09-27 · 115 rowsmo_census_county_pop_est— last synced 2026-10-15 · 6,440 rows