Spotting a good deal on a rental home can be difficult without knowing what rental homes in the area typically cost. But you can reduce the guesswork by mapping the U.S. Department of Housing and Urban Development’s latest small-area fair market rent estimates for local ZIP codes. Here’s a map shaded by each ZIP code’s estimated two-bedroom rent. Click or tap a ZIP code to see estimates for other rental home sizes in the ZIP code, plus the ZIP code’s Census-estimated number of occupied rental homes.
MTSU’s campus is the small, roughly rectangular shape near the center of the map, in the lower-left portion of ZIP code 37130. MTSU has its own ZIP code, 37132, but its rent estimates simply match those of the larger 37130 ZIP code.
A few perhaps useful observations:
The best combination of lower rents and a wide selection is probably in the 37130 ZIP code that surrounds MTSU’s campus.
The sparsely populated ZIP codes farther east have somewhat lower rest estimates but substantially fewer available rental homes - or homes of any kind.
ZIP codes elsewhere in Rutherford County generally have higher rents, fewer available rental homes, or both.
Especially in the ZIP codes around MTSU’s campus, multi-bedroom apartments or houses are often rented on a per-bedroom basis. So for a renter sharing a four-bedroom unit with three other renters, the monthly rent would be approximately a fourth of the four-bedroom rent shown.
Privacy is nice, but expensive. Generally, four people sharing the cost of a four-bedroom apartment will each pay less than will three people sharing the cost of a three-bedroom unit, who will pay less than two people sharing the cost of a two-bedroom unit, and one person paying rent on a single-bedroom or studio unit.
The area around MTSU’s campus is the most densely populated area in the county. Living farther from campus will generally mean living among less congestion. But it also will mean higher rent and a longer, more complicated commute to campus, especially in the mornings and late afternoons, when traffic around the county picks up.
Produced each year for selected ZIP codes in more densely populated areas of the country, small-area fair market rent estimates for an area reflect the monthly rent figure - with utilities included - below which the rent for 40 percent of standard quality rental housing units fall within the area.
HUD’s method for producing the estimates draws from the distribution of rents of all rental units in the area that are occupied by recent movers, excluding public housing units, newly built units, and substandard units. See the relevant part of the Code of Federal Regulations for details.
HUD uses these estimates to figure out how much money to provide in federal housing assistance. But you can use the same figures to get an idea of what a reasonable rent might be for a standard-quality rental unit in a given ZIP code. HUD provides estimates for different sizes of rental units, from a studio to a four-bedroom unit.
Students who take my Data Skills for Media Professionals course learn to produce this and other analyses of publicly available datasets using the R programming language. Here’s a step-by-step explanation of how R made the map.
To retrieve and map the data, R will need to install and load the following packages:
if (!require("dplyr")) install.packages("dplyr")
if (!require("tidyverse")) install.packages("tidyverse")
if (!require("mapview")) install.packages("mapview")
if (!require("leaflet")) install.packages("leaflet")
if (!require("leaflet.extras2")) install.packages("leaflet.extras2")
if (!require("tidycensus")) install.packages("tidycensus")
if (!require("sf")) install.packages("sf")
if (!require("openxlsx")) install.packages("openxlsx")
if (!require("scales")) install.packages("scales")
if (!require("leaflet.extras2")) install.packages("leaflet.extras2")
library(dplyr)
library(tidyverse)
library(ggplot2) #From the tidyverse package
library(readr) #From the tidyverse package
library(leaflet)
library(tidycensus)
library(sf)
library(mapview)
library(openxlsx)
library(leaflet.extras2)
library(leafpop)
library(scales)
options(tigris_use_cache = TRUE)
options(scipen = 999)
The script will need a list of the ZIP codes that you want to examine. This example uses a list of ZIP codes in, or mostly in, Rutherford County, Tennessee. Any valid list of any number of ZIP codes can be used. If you edit the list, just be sure to use quotes, commas and brackets in exactly the same way the example does. The lookup tool for a given year on HUD’s Small Area Fair Market Rents page can give you a list of usable ZIP codes for a given area. Note that ZIP codes can straddle county borders, and HUDs ZIP code list may not include all ZIP codes in a given area.
ZIPList <- c(
"37127",
"37128",
"37129",
"37130",
"37132",
"37085",
"37118",
"37149",
"37037",
"37153",
"37167",
"37086"
)
This portion of the script will retrieve HUD’s small-area fair market rent estimates for a given year, filter the data for the specified ZIP codes, then do some data paring, labeling, formatting, and data cleaning.
This example retrieves HUD’s 2025 dataset, which is available in
Excel format from the URL specified in the code: https://www.huduser.gov/portal/datasets/fmr/fmr2025/fy2025_safmrs.xlsx
.
To get the URL for a different year’s data, visit HUD’s
Small Area Fair Market Rents page, then copy and paste the URL for
the year you want.
FMR = read.xlsx(
"https://www.huduser.gov/portal/datasets/fmr/fmr2025/fy2025_safmrs.xlsx",
sheet = 1
)
FMR <- FMR[FMR$ZIP.Code %in% ZIPList, ]
keepvars <- c("ZIP.Code",
"SAFMR.0BR",
"SAFMR.1BR",
"SAFMR.2BR",
"SAFMR.3BR",
"SAFMR.4BR")
FMR <- FMR[keepvars]
colnames(FMR) <- c("ZIP", "Studio", "BR1", "BR2", "BR3", "BR4")
FMR$Zero_BR <- FMR$Studio
FMR$One_BR <- FMR$BR1
FMR$Two_BR <- FMR$BR2
FMR$Three_BR <- FMR$BR3
FMR$Four_BR <- FMR$BR4
FMR$Studio <- dollar(FMR$Studio)
FMR$BR1 <- dollar(FMR$BR1)
FMR$BR2 <- dollar(FMR$BR2)
FMR$BR3 <- dollar(FMR$BR3)
FMR$BR4 <- dollar(FMR$BR4)
# Deleting duplicate rows
FMR <- distinct(FMR)
ZIP codes can change over time, so the script uses the latest-available ZIP code map from the U.S. Census Bureau. Accessing the Census Bureau’s API requires an API authorization key, which you can get for free, and almost instantaneously, by completing and submitting the Census Bureau’s online Key Request Form. The key will arrive by e-mail and will be a long string of characters and numbers. The Census Burea expects you to keep your key private, the way you would a password. That’s why I’m not showing you my key here.
Once you have your key, paste it in place of PasteYourAPIKeyBetwenTheseQuoteMarks in the code below.
census_api_key("PasteYourAPIKeyBetwenTheseQuoteMarks")
Now, run this code to get the map. Initially, the map will contain every ZIP Code in the U.S. and its territories. The code will filter it for the ZIP codes you specified above. The code will toss in each ZIP code’s population estimate, because this particular Census API has to toss something in. It won’t give you a map and nothing else.
At present, the latest-available ZIP Code map is from 2021. If a more
recent year is available, change 2021
in
year = 2021
to the more recent year. If you’re not sure
whether a more recent year is available, simply give it a try. The
script will return an error if the newer year is still unavailable.
Note: The map will show Census ZIP Code Tabulation Areas, or ZCTAs, which resemble ZIP codes but may not exactly correspond to ZIP Codes as defined by the U.S. Postal Service. In short, the map will be close enough, but not exactly accurate.
ZCTAMap <- get_acs(
geography = "zcta",
variables = c(
Population = "B01001_001",
Rentals_ = "DP04_0047",
Rentals_P = "DP04_0047P"
),
year = 2021,
survey = "acs5",
output = "wide",
geometry = TRUE
)
# Filtering and formatting the map data
RCMap <- ZCTAMap[ZCTAMap$GEOID %in% ZIPList, ]
RCMap <- rename(RCMap,
ZIP = GEOID,
Rentals = Rentals_E,
Pct = Rentals_PE)
All R has to do now is merge the rent data from HUD with the map from the Census Bureau, then display the map.
By default, R will shade each ZIP code by its two-bedroom fair market
rent estimate. If you want to shade the ZIP codes by the fair market
rent for some other size of rental unit, change
zcol = "Two_BR"
to zcol = "Zero_BR"
,
zcol = "One_BR"
, zcol = "Three_BR"
, or
zcol = "Four_BR"
.
Once R displays the map, you can click the “stack of cards” icon in the top-left corner of the map, under the “+” and “-” zoom controls, to choose different base maps, including an OpenStreetMap, which gives a lot of details about the names of streets and roads, and an ESIR.WorldImagery map, which shows satellite imagery. The map is zoomable and moveable. Each area’s ZIP code will show as you hover over the area. Click on an area, and a table will pop up showing the area’s rent estimates for various sizes of units.
RCMap_plus <- RCMap %>%
left_join(FMR, by = c("ZIP" = "ZIP"))
mapviewOptions(basemaps.color.shuffle = FALSE)
ZIPMap <- mapview(
RCMap_plus,
zcol = "Two_BR",
col.regions = RColorBrewer::brewer.pal(9, "Blues"),
alpha.regions = .5,
layer.name = "Fair Market Rent",
popup = popupTable(
RCMap_plus,
feature.id = FALSE,
row.numbers = FALSE,
zcol = c("ZIP", "Studio", "BR1", "BR2", "BR3", "BR4", "Rentals", "Pct")
)
)
ZIPMap
Not everyone will need or want it, but some users might appreciate having the map’s data in .kml or .csv format, or both. The .kml-formatted data can be imported into a Google My Maps layer. The .csv-formatted data can be imported into Microsoft Excel, Google Sheets, or just about any other spreadsheet or data analysis application.
The script will save the files on your computer’s hard drive, in the
same subdirectory where the script is stored. The .kml file will be
called FMRMap.kml
, and the .csv file will be called
FMRData.csv
. Each time you run the script, the latest
versions of both files will overwrite any existing versions.
# Making a .kml Map File
st_write(RCMap_plus,"FMRMap.kml", append = FALSE)
## Writing layer `FMRMap' to data source `FMRMap.kml' using driver `KML'
## Writing 12 features with 18 fields and geometry type Multi Polygon.
# Making a CSV data file
RCMap_plus_data <- st_drop_geometry(RCMap_plus, drop = TRUE)
write_csv(RCMap_plus_data,"FMRData.csv")