New York City's housing market is one of the most closely watched — and most unequal — in the world. But aggregate statistics tend to flatten the texture of what's actually happening on the ground. A citywide median home value tells you very little about the difference between a two-bedroom in Tribeca and a row house in the East Bronx, or about which neighborhoods are appreciating fastest and which are softening.
The dataset at the center of this project addresses that granularity problem directly. Compiled from Zillow's housing data and published at jam9501.github.io/NYC_housing, it tracks estimated home values for 160 ZIP codes across all five NYC boroughs, comparing February 2025 to February 2026. Each ZIP code entry includes three figures: the Feb 2025 value, the Feb 2026 value, and the year-over-year percentage change.
The range is striking. On one end, ZIP code 10462 in the Bronx shows a median home value of $238,668 — on the other, 10013 in SoHo clocks in at $3.67 million. That's more than a 15x spread within the same city. The year-over-year changes are equally telling: the South Bronx ZIP codes 10451 and 10456 posted some of the highest growth rates in the dataset (9.1% and 11.2% respectively), while parts of Midtown Manhattan like 10018 and 10036 actually declined. This kind of data — granular, comparative, and temporally specific — is exactly what an interactive map is built to communicate.
The goal was a self-contained, embeddable web map that could be hosted on GitHub Pages and dropped into a Squarespace site via iframe. That ruled out heavier frameworks and pointed squarely toward Leaflet.js — a lightweight, battle-tested JavaScript mapping library with excellent GeoJSON support and a rich ecosystem. For the basemap tiles, I used CartoDB's dark theme, which provides enough geographic context without competing visually with the choropleth colors.
The first real challenge was sourcing polygon boundaries for NYC's ZIP codes. The NYC Open Data export API returned HTTP 400 errors on initial attempts, so I fell back to a GitHub-hosted GeoJSON that proved reliable and came with a useful bonus: a PO_NAME field containing neighborhood names like "Jackson Heights" or "Flatbush," which I was able to surface in the hover tooltips.
The map offers two ways to color the ZIPs: by Feb 2026 home value and by year-over-year percentage change. Each required a different color design approach.
For home values, the naive approach — a linear color scale from minimum to maximum — produced a nearly monochromatic map. The problem is that housing values in NYC are heavily right-skewed: most ZIPs cluster in the $400K–$1.2M range, while a handful of Manhattan ZIPs push $2M–$3.7M. A linear scale wastes most of its dynamic range on those outliers. The fix was a log-normalized scale: by mapping log(value) rather than value to the 0–1 color range, the color differences in the common price bands become much more visible. I paired this with a seven-stop palette running from deep purple (cheapest) through blue, cyan, teal, yellow, and orange to dark red (most expensive).
For year-over-year change, I needed a diverging scale — red for declining values, green for growth, with neutral gray anchored at zero. Rather than a symmetric design, I scaled each arm independently to the actual min (−6.96%) and max (+11.55%), so the full color range is used on both sides regardless of whether gains or losses are larger.
Subway system. Lines came from a GitHub-hosted GeoJSON and stations from a converted NYC Open Data set. Each line is drawn in its official MTA color — the 1/2/3 in red, the A/C/E in blue, the N/Q/R/W in yellow, and so on — using a lookup table built from the MTA's standard palette. Station markers are small circles colored to match their primary line, with tooltips showing the station name and all served lines.
Public parks. NYC Parks Department properties came from the NYC Open Data Socrata API, which returned about 4,600 polygon features covering everything from Flagship Parks like Central Park down to small triangles and plazas. These render as semi-transparent green polygons with hover highlighting and tooltips showing the park name, type category, and acreage.
Median household income by census tract. This was the most technically involved overlay, because it required joining two separate datasets in the browser at runtime. The 2020 census tract boundaries came from NYC Open Data, which includes a geoid field — the standard 11-digit Census FIPS identifier. The income data came from the Census Bureau's ACS 5-year 2023 API, fetching variable B19013_001E (median household income) for all census tracts in the five NYC counties. I constructed the GEOID from the API response fields and joined the two datasets on that key entirely in JavaScript, with no backend required.
The income choropleth uses the ColorBrewer YlGnBu palette — pale yellow for lower incomes through teal to navy for the highest — again log-normalized. One design detail: when the income overlay is toggled on, the ZIP choropleth automatically dims to 22% fill opacity (from its normal 82%), so the census tract colors are clearly visible while the ZIP boundaries remain as light context lines. Toggling income off restores the ZIP layer to full opacity.
The legend panel in the lower right updates dynamically based on which mode is active. When the income overlay is toggled on, a second color bar appears above the home value bar, labeled with the actual min, log-midpoint, and max income values from the fetched data. Toggling the overlay off removes it again.
The finished map is a single index.html file with all data embedded and all dependencies loaded from CDNs. It is hosted on GitHub Pages and can be embedded on any page with a simple <iframe> tag.
With all the layers on at once, the map tells a layered story. The wealthiest ZIP codes cluster in lower and midtown Manhattan, the western Brooklyn waterfront, and pockets of northeastern Queens — broadly following the subway lines that give those neighborhoods the best access to jobs. The fastest-appreciating areas tend to be in the South Bronx and central Brooklyn: neighborhoods where values are still relatively low by NYC standards but where demand is climbing.
The income overlay makes the underlying correlation legible at a finer grain than ZIP codes alone can capture, with many census tracts showing sharp income gradients even within a single ZIP code. The parks overlay adds one more dimension: a useful reminder of how much of the city's land area is actually green space, and how unevenly that green space is distributed relative to where the housing pressure is highest.
Zillow data • Feb 2025 → Feb 2026 • 160 ZIP codes • Source: jam9501.github.io/NYC_housing
Loading ZIP code boundaries…