What this map measures, what it cannot tell you, and the choices behind both.
U.S. Census Building Permits Survey; 2010 Decennial Census
1. What the map measures
For every Illinois municipality whose permit office reports to the Census:
Percent growth = housing units authorized by permit, 2010–2025
÷ housing units counted in that municipality on April 1, 2010
The numerator is the Census Bureau's Building Permits Survey — administrative counts collected from permit-issuing jurisdictions, not a sample or a survey estimate. The denominator is table H1 of the 2010 Decennial Census, the complete count, not an American Community Survey estimate.
The two do not overlap. A unit standing on April 1, 2010 was authorized before that date, so it is in the denominator and not in the numerator. Permits are summed from calendar year 2010 forward.
—Illinois, since 2010
—United States, since 2010
—municipalities with no 5+ unit building permitted
The Illinois figure is the midpoint of the map's color scale, so the color break means "keeping up with the state." Orange is below it, blue above.
2. What these numbers are not
The chart goes back further than the metric does. Every municipality's chart runs from 2000 so the pre-2008 baseline is visible, but only 2010 onward feeds any figure on this page. The earlier years are drawn faded, with a dashed outline, behind a marked boundary.
Not units built.
BPS counts units authorized by permit. A permitted project that was never financed, never started or abandoned still counts here. This is the standard measure of how much housing a jurisdiction has allowed, which is the question in front of the legislature — but it is not a count of completed homes.
Not every home that was added.
The survey counts permits for new buildings. Converting a house into apartments, adding a story, or legalizing a basement apartment never shows up. Nor does a permit the office did not report: when an office skips months, the Census fills them in with its own estimate, and that estimate can miss a real building. Berwyn is an example. reVerb Century Station, a 52-unit building finished around 2010, appears nowhere in Berwyn's permit record, which was filed for only 3 months a year in 2004–2006. And the Berwyn Apartments' 2018 renovation into 28 apartments is a conversion the survey never counts. For how much housing exists, the census count is the better number — see section 5.
Not net change in the housing stock.
Demolitions are not subtracted. A municipality that permitted 500 units and demolished 400 appears here as 500. Read every number as "how much was added," never as "how much the stock grew."
Not a measure of need, cost or affordability.
Nothing here says whether the housing was affordable, who it was for, or whether it met demand. Rent and cost data were deliberately left out of this version.
Not a statement about unincorporated territory.
See the design decisions below — county-filed permits are excluded, so this is a map of municipalities, not of all Illinois land.
3. A coverage gap is never a zero
This is the single most important thing to understand before quoting the map.
Not every Illinois place has a permit office reporting to the Census. — of the state's — places do; — do not.
A municipality with no permit office reporting is not a municipality that built nothing. It is one the Census has no administrative record for. Those places are drawn with a gray hatch — never with a low-value color — are excluded from the table, from rankings and from every statewide total, and show an explanation instead of metrics when opened.
The flat, unpatterned land between municipalities is something different: unincorporated Illinois, which belongs to no municipality at all. It is filled in so the state reads as one piece of territory rather than as scattered municipalities on a blank page, but it carries no value — permits filed there are county permits, covered under “County-filed permits are not on the map” below. The legend shows all three: a color is a measurement, the hatch is a municipality with no permit office, and the flat, unpatterned fill is land with no municipality.
The same rule applies year by year. A municipality whose permit office first reported in 2013 has no value — not zero — for 2010 through 2012. Its chart shows a shaded gap over those years, and its detail panel says which year its record begins. — reporting municipalities do not have a record for every year in the window.
And a third level, added after Austin Busch asked about Cicero. A permit office that is in the survey can still file for only part of a year — or for none of it. The Census publishes a figure either way: for a non-reporting office it publishes its own estimate. Those are real published numbers, so the map shows them, but they are not counts the municipality made.
Of the reporting municipalities, — reported all twelve months in every year since 2010. — reported no month at all across the whole window; for those, every figure is an estimate, so they carry no "no 5+ unit" flag and are left out of that count. A municipality's detail panel names the years its office did not report, and the chart puts a tick under them.
If you are about to say "this municipality built nothing," check that its coverage is reporting, check the first year of its record, and check whether its office actually filed. The map will tell you all three.
4. What "no 5+ unit" means, and what it does not
The Building Permits Survey splits every unit into four buckets by the size of the building it sits in: single-family, 2-flat, 3- or 4-flat, and five-or-more.
The headline highlight — No 5+ unit buildings — counts only the last bucket. A municipality can appear in it and still have permitted a 2-flat, a 3-flat or a 4-flat. Glen Ellyn is an example: it has permitted no units in buildings of five or more since 2010, and four units in 3- and 4-flats. Both statements are true at once.
So there are two switches, not one. — municipalities have permitted no 5+ unit building since 2010; — have permitted nothing above a 2-flat. The second is the stricter claim and the safer one to make in testimony if what you mean is "nothing that looks like an apartment building."
Every municipality's detail panel prints all four buckets, so the split is never hidden behind the flag.
5. Permits vs. what the 2020 Census counted
A permit is a plan to build. The 2020 Census counted what was standing. Every municipality's detail panel, and a column in the table, compares the two for the decade between the counts.
The figure is the change in the housing count from April 1, 2010 to April 1, 2020, minus the units permitted in 2010–2019. It is shown in housing units, not as a ratio: in — of the municipalities where it can be computed, the housing count fell over the decade, and a ratio with a shrinking count in it stops meaning anything.
Illinois as a whole permitted — units in that decade while its housing count rose by — — a difference of —.
The two numbers do not have to match, and the gap is not a score. Demolitions and conversions lower the count but never the permit total. Not every permitted unit gets built, and one permitted in late 2019 may not be standing by April 1, 2020. Annexation moves a municipality's boundary between the two counts. And each count has its own error. A large gap is a question to ask a municipality, not a finding about it.
It is shown only where the permit office reported all twelve months of all ten years — otherwise the permit side is partly the Census's own estimate for a non-reporting office. That leaves — municipalities with the figure and — reporting municipalities without it; their panels say which years fell short.
How far apart they get. Among the — municipalities with a fully reported decade, the count rose by more than 5% of the 2010 stock beyond what permits explain in —, and fell more than that short of them in —. Those municipalities get a flag in their panel. A count that rose far past the permits usually means conversions or unreported permits; one that fell short usually means demolitions or homes lost. Either way, for how much housing exists, the count is the one to trust. Permits remain the measure of what a municipality allowed as new construction, which is the question this map is built to answer.
The 2020 count is used for this comparison and nothing else. Percent growth, everywhere on the map, is still measured against the 2010 count.
6. By legislative district
The legislator view shows every State Senate and House district, the municipalities it takes in, and the member's contact details.
How a municipality is matched to a district. By land area, on the Census's full-detail boundaries: a municipality is listed under a district when at least 1% of its land falls inside it, and the share is printed beside it. Below 1% the overlaps are slivers where two boundaries do not quite meet. A municipality split between districts is listed under each of them — Arlington Heights sits entirely in one Senate district but is split 70/30 between two House districts, and Chicago spans 19 Senate and 33 House districts.
The district total is an estimate. Permits are counted by municipality, not by district, so a district's total counts each municipality's permits in proportion to the share of its land inside the district. That assumes a municipality's permits are spread evenly across it, which they rarely are exactly. It is printed rounded, with an "estimate" tag. The municipality rows beneath it are exact. Permits filed by a county for unincorporated land are not in it, and municipalities with no permit office add nothing to it and are named as such.
Where the names come from. District boundaries are the Census's current cartographic files, drawn for the map in force since the 2022 election. Members and their contact details come from Open States' list of current Illinois legislators, a public file refreshed as members change; the page shows the date it was retrieved. A member listed wrongly is corrected by hand in data/manual/legislator_overrides.csv, never filled in from memory.
The printable sheet. Each district's panel has a "Print one-page sheet" button, and printing an open district from the browser's own menu does the same: one Letter page to leave behind in a meeting, with the member, the district's totals, a QR code back to the district's page, a map, and up to four featured municipalities, numbered on the map and on the page. The browser's "Save as PDF" makes a PDF of it. The municipalities are chosen by a fixed rule, not by hand:
The municipality with the member's district office first, when that office is in a municipality inside the district.
Then the best of the rest, up to four in all. When there is no district office to start from (no office listed, an office listed only in Springfield, or an office outside the district), all four are the best municipalities.
"Best" means municipalities with more than half their land in the district and more than 5,000 people first, then everyone else; within each group, by share of land inside × population, roughly the number of its residents who live in the district.
Chicago counts as one municipality. Census-designated places, which have no municipal government, are never featured. A district with fewer than four municipalities shows all of them.
The Affordable Housing Planning and Appeal Act asks the Illinois Housing Development Authority (IHDA) to score every local government on how much of its housing is affordable. —
Affordable housing share
IHDA's count of year-round homes a household could afford — to buy at 80% of the regional median income, or to rent at 60% — divided by all year-round homes. It is IHDA's figure, shown as published, and it comes from Census survey estimates for 2017–2021, not from the permit data on the rest of this site.
Non-exempt
A municipality under 10% is non-exempt, subject to exceptions the Act sets. The main one: IHDA treats a municipality of fewer than 1,000 residents as exempt whatever its share, so a few very small villages are exempt below the line. On the map, the "AHPAA non-exempt only" switch shows just those —; the table and every municipality's panel show the status and the share.
Under 25% affordable
A stricter line that has been proposed but is not the Act's: under 25% affordable, among municipalities of more than 2,000 people. The "Under 25% affordable" switch shows those —. Population here is the 2020 Census count used everywhere else on the site; IHDA's own population estimates pick out exactly the same municipalities.
Where the list comes from
IHDA's 2023 Statewide Report on Local Government Affordability, the current report. Impact for Equity supplied it as a spreadsheet, and every row was checked against IHDA's published PDF — population, homes, affordable homes and share — and the non-exempt rows against IHDA's separate non-exempt list. All matched. Nothing in it was typed in or filled from memory.
Each IHDA row is attached to the Census municipality with exactly the same name, never a near match: the looser matching used elsewhere drops words like "City", which would have put Mason City's figures on Mason.
What it does not cover
A place with no IHDA row shows no status, never "exempt." That is every Census-designated place, which is not a municipality, and Cahokia Heights, formed in 2021 from Cahokia, Alorton and Centreville after the years IHDA used. IHDA scored those three separately, and their rows cannot be attached to a municipality that did not exist yet. Clear Lake and Time are on IHDA's list but not in the Census's 2025 boundaries. All five were exempt.
Sandoval's share is incomplete. IHDA notes that property taxes, which it normally counts against what a homeowner can afford, were not available for Sandoval, so its calculation leaves them out.
IHDA's population is not shown. It is a survey estimate and can differ from the 2020 Census count used everywhere else here. It matters once: Timberlane is 1,323 in IHDA's report and 906 in the Census. IHDA lists it as non-exempt, and so does this site, because that is IHDA's determination; on the Census count it would fall under the small-village exemption. Its panel says so.
To load the next report, replace the spreadsheet in data/manual/ and run uv run scripts/import_ahpaa.py, then rebuild. The import stops, rather than guessing, if a name no longer matches a Census municipality.
8. Design decisions
Settled choices, not waiting on anyone. Each is a place where the sources left a gap and the build had to pick a rule; they are recorded here so a reader can see what was picked and why.
County-filed permits are not on the map
About 8% of Illinois permits since 2010 are filed by county permit offices for unincorporated territory, plus a few whole-county and township offices. That territory is not a municipality and has no place boundary to draw, and a county-balance layer was out of scope for this version. On the map it is the flat, unpatterned land between the municipalities.
Those units are excluded from every municipal figure and are itemised line by line in data/processed/unmatched.csv. The consequence for testimony: statewide totals on this map are municipal totals. They are smaller than Illinois' true permit count, by that 8%.
Decided and documented, not outstanding. Adding the layer is a v2 question.
Three municipalities have no boundary to draw
Centreville, Cahokia village and Gulf Port village carry real FIPS place codes in the permit files, but those codes no longer exist in the 2025 Census boundary file — the municipalities were dissolved or merged. Attributing their permits to a successor municipality would need a municipal-succession record, which is not one of this project's sources, and guessing the link would credit one municipality with another's permits.
They remain unmatched and their units are excluded: 3 units across 2010–2025, out of 272,726. The successor municipality shows only the permits filed under its own record.
Unresolved by design. Negligible in size, but recorded rather than papered over.
A 0.0071% discrepancy whose cause is not established
Summing the place-level permit files gives 297,273 Illinois units for 2010–2025. The Census Bureau's own published state-level file gives 297,252 — a difference of 21 units, all single-family, spread over five years. Eleven of the sixteen years reconcile exactly.
It was investigated and not explained. It is not a column-reading error (that would be wrong by orders of magnitude, and could not be exact in eleven years). It is not the universe-change footnote in the Census documentation (no Illinois record carries that marker). The likeliest remaining explanation is that the place-level and state-level files are revised on different schedules — but nothing in the documentation says so, so it is recorded as a hypothesis, not a finding.
The headline Illinois rate on this site uses the Census-published state figure. The per-municipality numbers come from the place files. The gap is roughly seventy times smaller than the tolerance the build allows.
Logged, not chased. No effect on any municipality's number.
Some county labels are derived, not read
A municipality with a permit record gets its county from that record. A municipality without one has no such field, and none of this project's sources is a place-to-county lookup — so its county label is derived geometrically, by testing the municipality's interior point against the Census county boundaries.
That is a computation on published boundaries, not an estimate, but it is not authoritative either: a municipality straddling a county line is labeled with whichever county contains its interior point. Every municipality's detail record carries which method produced its label.
Resolved, with the method recorded per municipality.
9. How the numbers are checked
Because this is meant for testimony, the build refuses to declare itself finished unless a set of checks passes, and it prints the full report either way. They are not smoke tests — several of them re-read the raw Census files independently rather than trusting anything the build wrote.
Every Illinois place appears exactly once, and every one has a coverage status.
At least 95% of municipal permit records resolve to a boundary. The actual figure is —, and the records that do not resolve are printed, largest first.
Every percentage has a real 2010 denominator behind it; none is negative; anything above 200% is flagged for a human to look at.
Statewide totals rebuilt from the per-municipality files reconcile with the raw Census rows, with every unit accounted for.
The permit-file column positions are verified against the column headers Census ships inside the files and against Census's independently published state totals — because the 2000–2006 files use an older layout that the current documentation does not describe.
Every permits-vs-2020 figure is recomputed from the raw Census files, and none may exist for a municipality whose permit office missed a month in the decade.
Every municipality has a Senate and a House district, every district has a member and at least one municipality, and each district's list and estimate are recomputed from the boundary files.
The page itself is driven in a real browser: it must load with zero console errors, render the map, show a numeric state average, open a municipality's detail panel with a percentage and an absolute count, and restore a shared link.
A check was never loosened to make it pass. Where a check could not be satisfied as literally written — see the unincorporated-permits item above — the reason is documented and both the strict and the achievable figure are printed side by side.
10. Rebuilding it
Every number on this site is reproducible from raw public sources by running one command. From a fresh copy of the repository:
uv sync
uv run scripts/build.py
uv run scripts/check_data.py
The build needs no network access — the downloaded Census files are kept in the repository. To refresh them when new annual data is published, run the four fetch scripts first. Every URL, with the date it was retrieved and the file it produced, is recorded in data/SOURCES.md.