cori.data.bps
Access and analyze U.S. Census Bureau Building Permits Survey (BPS) data covering new residential units authorized from 2000 to present. Tracks housing supply growth at the county, state, and national level as an indicator of local market conditions and development capacity.
Drew Rosebush 2026-08-31
Overview
The cori.data.bps package provides access to U.S. Census Bureau Building Permits Survey (BPS) data at the county, state, and national level from 2000 to the present. BPS tracks new residential units authorized by building permits across all structure types — single-unit, two-to-four unit, and five-or-more unit — providing an annual measure of how housing supply is growing in response to local conditions.
Building permits are the earliest observable signal of housing supply responding to conditions on the ground — and in communities where housing is a binding constraint on growth, that signal matters. Permit counts are normalized by population using cori.data.pep and returned in the standard cori.data long format: one row per geoid / year / variable.
Installation & quick start
remotes::install_github("ruralinnovation/cori.data.bps")
library(cori.data.bps)
# All available variables
get_bps_codebook()
# All counties, all years
get_building_permits(geography = "county")
# New Hampshire counties, permits per 1k population, 2010 onward
get_building_permits(
variables = "units_per_1k_people",
geoids = c("33009", "33011"),
years = 2010:2024
)
What this data tells us about rural America
Housing supply is a foundational asset for rural economic development — and in many communities, a binding constraint on growth strategies that are otherwise well-designed. CORI’s place-based community research has found communities with genuine competitive assets — skilled institutions, entrepreneurial infrastructure, strong broadband — where housing shortfall functions as a ceiling on everything else. Workers cannot relocate to communities where housing is unavailable or unaffordable, and population retention becomes harder when housing stock does not keep pace with demand.
Building permit data captures the rate at which housing supply is responding to conditions on the ground. An increase in permit activity indicates that supply is growing in response to demand, and that local regulatory and market conditions allow it. Where permit activity lags population or employment growth, the data surfaces a constraint that deserves attention alongside other economic development investments.
Introduction
Adapted from the Introduction vignette on GitHub.
cori.data.bps provides annual building permit data from the U.S. Census Bureau’s Building Permits Survey. Data cover 2000–present and measure new residential units authorized by building permits across all structure types.
Source: U.S. Census Bureau, Building Permits Survey Coverage: 2000–present, updated annually (release typically May) Geography: County (5-digit FIPS), state (2-digit FIPS), national ("00")
Variables
get_bps_codebook() |>
dplyr::select(variable, label, unit, notes) |>
knitr::kable()
Reading data
All data are returned in long format: one row per geoid / year / variable.
df <- get_building_permits(geography = "county")
dplyr::glimpse(df)
Filter to specific variables, years, or geographies:
nh_bps <- get_building_permits(
variables = "units_per_1k_people",
geoids = grep("^33", unique(df$geoid), value = TRUE),
years = 2010:2024
)
dplyr::glimpse(nh_bps)
Rural vs. nonrural
Housing permit activity diverges between rural and nonrural counties. The chart below shows population-weighted average units permitted per 1,000 people by rural status using the CBSA 2023 rural definition.
library(cori.charts)
library(ggplot2)
library(ruraldefinitions)
library(dplyr)
load_fonts()
rural_avg <- df |>
filter(variable == "units_per_1k_people") |>
left_join(select(cbsa_2023, geoid, is_rural), by = "geoid") |>
filter(!is.na(is_rural), !is.na(value), !is.na(agg_var)) |>
group_by(year, is_rural) |>
summarise(
value = sum(value * agg_var, na.rm = TRUE) / sum(agg_var, na.rm = TRUE),
.groups = "drop"
)
ggplot(rural_avg, aes(x = year, y = value, color = is_rural)) +
geom_line(linewidth = 1) +
geom_point(size = 2.5) +
scale_color_manual(
values = c("Rural" = "#2F6E9B", "Nonrural" = "#7EBDC2"),
labels = c("Rural" = "Rural counties", "Nonrural" = "Nonrural counties")
) +
scale_x_continuous(breaks = seq(2000, 2024, by = 5)) +
scale_y_continuous(labels = scales::label_number(accuracy = 0.1)) +
theme_cori() +
theme(legend.position = "bottom") +
labs(
title = "Nonrural counties consistently authorize more housing per capita",
subtitle = "New residential units authorized per 1,000 population, 2000\u20132024",
x = NULL, y = NULL, color = NULL,
caption = "Source: CORI analysis of U.S. Census Bureau Building Permits Survey."
)
County spotlight: Grafton County, NH
grafton <- get_building_permits(
variables = "units_per_1k_people",
geoids = c("33009", "33", "00")
) |>
mutate(
group = case_when(
geoid == "33009" ~ "Grafton County, NH",
geoid == "33" ~ "New Hampshire",
geoid == "00" ~ "United States"
),
group = factor(group, levels = c("Grafton County, NH", "New Hampshire", "United States"))
)
ggplot(grafton, aes(x = year, y = value,
color = group, linetype = group, linewidth = group)) +
geom_line(na.rm = TRUE) +
geom_point(size = 2.5, na.rm = TRUE) +
scale_color_manual(values = c(
"Grafton County, NH" = "#2F6E9B",
"New Hampshire" = "#7EBDC2",
"United States" = "#9DA7B0"
)) +
scale_linetype_manual(values = c(
"Grafton County, NH" = "solid",
"New Hampshire" = "dashed",
"United States" = "dotted"
)) +
scale_linewidth_manual(values = c(
"Grafton County, NH" = 1.2,
"New Hampshire" = 0.8,
"United States" = 0.8
)) +
scale_x_continuous(breaks = seq(2000, 2024, by = 5)) +
scale_y_continuous(labels = scales::label_number(accuracy = 0.1)) +
theme_cori() +
theme(legend.position = "bottom") +
labs(
title = "Grafton County, NH\nNew residential units authorized per 1,000 residents",
subtitle = "Compared to New Hampshire and the United States, 2000\u20132024",
x = NULL, y = NULL, color = NULL, linetype = NULL, linewidth = NULL,
caption = paste0(
"Source: CORI analysis of U.S. Census Bureau Building Permits Survey.\n",
"Population denominator from Census Population Estimates Program via cori.data.pep."
)
)
Related
- Dataset: Census Building Permits
- Package: cori.data — meta-package that includes
cori.data.bps - Package: cori.data.pep — population denominators used to compute permits per 1,000 people
- Package: cori.data.hu — total housing stock; pairs with BPS to distinguish new supply from existing stock
- Project: Rural Economic Outlook — BPS is an input to CORI’s housing supply analysis
NOTE Data attribution: Building Permits Survey data are produced by the U.S. Census Bureau. When citing analysis using this package, reference: U.S. Census Bureau, Building Permits Survey, www.census.gov/construction/bps.