DEV Community

Cover image for Realtor.com API in Python: Are New Homes Really Pricier per Square Foot?
Happy Endpoint
Happy Endpoint

Posted on Originally published at Medium

Realtor.com API in Python: Are New Homes Really Pricier per Square Foot?

New single-family homes in Austin list at a median of $218 per square foot. Resale homes in the same Realtor.com search ask $304. On those two numbers alone, a new build is 28% cheaper than a used one, which is not what most buyers walking through a model home would expect.

I wanted to know whether that holds up, so I pulled new construction and resale listings for nine US metros with Python and compared them three ways: by city, by size band and inside the same ZIP code. The short version: the city numbers lie, and the reason they lie is the most useful thing in the data.

The data

Everything here comes from the Realtor.com Data API, which returns Realtor.com search results as JSON. On 24 October 2026 I ran three searches for each of nine cities: Austin, Houston, Phoenix, Charlotte, Atlanta, Nashville, Denver, Jacksonville and Raleigh.

  • New construction: /properties-for-sale with property_type=single_family, new_construction=true and hide_homes_not_yet_built=true, so only homes that exist or are being built, with a real address.
  • Resale: the same search with hide_new_construction=true instead.
  • Floor plans: one page of /new-construction, which turned out to be a different thing entirely (more on that below).

Both listing searches were sorted newest and I took two pages of 50, so each city contributes its 100 most recently listed homes of each kind. That is 2,195 rows from 45 search requests. After removing duplicates and listings without a price or square footage, 855 new homes and 847 resale homes were left. This is a snapshot of fresh inventory, not the whole market: the same searches report 6,735 new and 34,302 resale single-family listings in total across the nine cities.

The code

This is the core of the analysis. It runs as printed with requests and pandas on Python 3.9 or later, once RAPIDAPI_KEY is set in your environment. Every response is cached in cache/, so you pay for each page once and can rerun the analysis for free.

import json
import os

import pandas as pd
import requests

HOST = "realtor-com-data-api.p.rapidapi.com"
HEADERS = {"X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"], "X-RapidAPI-Host": HOST}
METROS = ["Austin, TX", "Houston, TX", "Phoenix, AZ", "Charlotte, NC", "Atlanta, GA",
          "Nashville, TN", "Denver, CO", "Jacksonville, FL", "Raleigh, NC"]
BASE = {"property_type": "single_family", "sort": "newest", "limit": 50}
GROUPS = {
    "new": dict(BASE, new_construction="true", hide_homes_not_yet_built="true"),
    "resale": dict(BASE, hide_new_construction="true"),
}
os.makedirs("cache", exist_ok=True)


def search(name, params):
    """One page of /properties-for-sale, cached on disk so reruns are free."""
    path = os.path.join("cache", name + ".json")
    if not os.path.exists(path):
        r = requests.get("https://%s/properties-for-sale" % HOST,
                         headers=HEADERS, params=params, timeout=60)
        r.raise_for_status()
        with open(path, "w") as f:
            json.dump(r.json(), f)
    with open(path) as f:
        return json.load(f)["data"]


rows = []
for metro in METROS:
    slug = metro.lower().replace(", ", "-").replace(" ", "-")
    for group, params in GROUPS.items():
        for page in (1, 2):
            p = dict(params, location=metro)
            if page > 1:
                p["page"] = page
            for home in search("%s_%s_p%d" % (group, slug, page), p)["results"]:
                rows.append({
                    "metro": metro,
                    "group": group,
                    "property_id": home["property_id"],
                    "zip": home["location"]["address"]["postal_code"],
                    "price": home["list_price"],
                    "sqft": home["description"]["sqft"],
                })

df = pd.DataFrame(rows).drop_duplicates(["metro", "group", "property_id"])
df = df[(df.price > 0) & (df.sqft >= 400)]
df["ppsf"] = df.price / df.sqft

table = df.pivot_table(index="metro", columns="group", values="ppsf", aggfunc="median")
table["premium_%"] = (table.new / table.resale - 1) * 100
print(table.round(1).sort_values("premium_%"))

z = df.groupby(["metro", "zip", "group"]).ppsf.agg(["median", "count"]).unstack("group")
z = z[(z["count"]["new"] >= 3) & (z["count"]["resale"] >= 3)]
zip_premium = (z["median"]["new"] / z["median"]["resale"] - 1) * 100
print("%d ZIP codes, new dearer in %d, median premium %.1f%%"
      % (len(zip_premium), (zip_premium > 0).sum(), zip_premium.median()))
Enter fullscreen mode Exit fullscreen mode

Two lines in there carry most of the weight. drop_duplicates on property_id matters far more in one city than you would guess, and sqft is the living area; the response also has lot_sqft, and dividing by that gives you a number that means nothing. The longer script I used for the charts and the data checks adds the floor plans, the size bands and the counts quoted in the pitfalls section.

City by city, new homes look cheap

Pooled across all nine cities, the median new home asks $239 per square foot and the median resale home $258, so new construction comes out 7.1% cheaper. City by city, new homes are cheaper per square foot in five of the nine.

New construction lists for less per square foot than resale in 5 of 9 metros: 28.1% less in Austin and 23.0% less in Raleigh, but 22.5% more in Denver and 17.2% more in Atlanta.

Metro Resale $/sqft New $/sqft Gap Gap at 1,500 to 2,500 sqft New share
Austin, TX $304 $218 -28.1% -24.8% 19.8%
Raleigh, NC $254 $195 -23.0% -14.0% 24.7%
Nashville, TN $300 $273 -8.8% +1.8% 18.7%
Phoenix, AZ $286 $273 -4.3% -11.0% 11.8%
Charlotte, NC $228 $222 -2.3% -4.0% 17.7%
Jacksonville, FL $185 $192 +3.4% -6.0% 20.5%
Houston, TX $173 $194 +11.9% +17.7% 18.6%
Atlanta, GA $226 $265 +17.2% +3.7% 9.8%
Denver, CO $376 $461 +22.5% +24.4% 5.0%

The spread is wide. Austin and Raleigh new homes ask roughly a quarter less per square foot than resale. Denver and Atlanta go the other way by about a fifth. A national "new construction premium" averaged over these cities would hide all of it.

Is it just size?

The obvious objection: price per square foot falls as homes get bigger, and new homes are bigger. They are. The pooled median new home has 2,258 sq ft against 1,950 for resale, and in Atlanta the gap is 3,251 against 1,925.

So I reran the comparison using only homes between 1,500 and 2,500 sq ft in both groups. New homes were still cheaper per square foot in five of nine cities. Austin barely moves (from -28.1% to -24.8%). Nashville flips to slightly more expensive, Jacksonville flips to slightly cheaper. Size explains some of the gap in some cities. It does not explain Austin or Raleigh.

It is location

What the city-level comparison really measures is where builders can find land. The location search covers the whole city, so a new home on the edge of town is compared with a resale home in an older, closer-in neighborhood. To take that out, I compared new and resale homes only inside the same ZIP code, keeping ZIPs with at least three of each.

That leaves 54 ZIP codes. In 31 of them, new homes ask more per square foot than resale homes. The median premium is 6.1%, and 15 of the 54 ZIPs show a premium above 20%.

Inside the same ZIP code, new homes usually cost more per square foot: in 31 of 54 ZIP codes the new-construction premium is positive, and in 15 it is above 20%.

The concentration is striking once you look at it. In Raleigh, 61 of the 99 new homes in my sample sit in a single ZIP code, 27616. In Jacksonville, 36 of 99 are in 32218, and in Austin 34 of 100 are in 78747. Those are the places where tract builders are working, and they are not the places the median resale listing comes from. Inside 32218, new homes ask 21.0% more per square foot than resale. In Charlotte's 28278, where 20 of the new homes are, the premium is 30.4%.

So the honest answer to the title is: yes, a new home usually costs more per square foot than a comparable used home nearby. It just tends to be built somewhere cheaper, and a citywide median mixes the two effects.

Where builders build

Every search also returns a total, the number of matching listings, which costs nothing extra and shows how different these markets are. Dividing the new-construction total by the new plus resale totals, new construction is 24.7% of single-family homes for sale in Raleigh, 20.5% in Jacksonville and 19.8% in Austin, but only 9.8% in Atlanta and 5.0% in Denver.

New construction is a quarter of the single-family homes for sale in Raleigh but only 5% in Denver.

The two cities where new construction is rarest, Denver and Atlanta, have the two largest premiums, and two of the three where it is most common, Raleigh and Austin, have the two largest discounts. Denver looks like infill: its resale homes have a median year built of 1955, and it produced 11 of my 54 matched ZIP codes, more than any other city, so new and old homes share neighborhoods. Its new homes have a median of 3,212 sq ft and a median list price of $1,412,000, against 2,080 sq ft and $697,500 for resale. Large new houses in established neighborhoods are expensive per foot, and the city median shows it.

Pitfalls I hit in the data

This is the part I would want to read before running the script myself.

/new-construction is mostly floor plans, not houses. All 395 rows from that endpoint had status set to ready_to_build and the is_plan flag set. None of them had a year_built or a lot_sqft, and 25 had no price at all. Because I called it without a property_type, 101 of the 395 were not single-family (townhomes, condos and multi-family plans). A plan is a design a builder offers, so treat its price as an asking price for a design, not for a home on a lot. For comparisons with resale, the new_construction=true filter on the for-sale search, with hide_homes_not_yet_built=true, gives you homes with addresses. Where both existed, single-family plan prices per square foot were within 5% of the built new homes in six of the eight cities that had plans.

Builder listings look different from MLS listings. 60.9% of the new homes had no mls_status, and every one of those had a community advertiser: the builder's community listed them, not an agent through the MLS. These are thinner records: year_built was empty for 46.6% of new homes and set to a year after 2026 for 8.3% (homes still being built). lot_sqft was missing for 67.7% of new homes against 1.1% of resale homes, and only 33.6% of new homes carried a value estimate against 86.6% of resale. Any model that leans on lot size or estimates will quietly drop most new construction.

Atlanta lists the same house twice. I removed 78 duplicate property_id values, and 64 of them were in Atlanta (28 new, 36 resale). Looking up one of them with /property-details showed why: its property_history has two "Listed" events on 23 October 2026 at the same $299,900, one from GeorgiaMLS and one from FMLS. Atlanta listings can be entered in both of those MLSs, and a home entered in both comes back from search as two listings with two listing_id values. Without deduplicating on property_id, Atlanta's medians are built on a sample where a third of the rows are counted twice. Most of the remaining duplicates appeared on both page 1 and page 2, which is what happens when new listings arrive between two requests on a newest-first sort.

The search city is not the address city. 8.7% of all rows had an address city different from the city I searched, such as Manor or Pflugerville in the Austin search. Realtor.com's location search follows its own area boundary, so if you need municipal limits, filter on location.address.city yourself, or use postal_code.

Newest-first brings in coming-soon and pending homes. 76 resale and 18 new homes in the sample were flagged is_coming_soon, and 29 were pending or contingent. I kept them, because they still carry an asking price, but checked the result without them: new construction is still cheaper per square foot in five of nine cities, and Denver's premium moves from 22.5% to 19.6%.

Search results have no price per square foot. /property-details returns a price_per_sqft field (184 for the Atlanta home above, which is $299,900 divided by 1,632 sq ft, rounded), but search results do not, so you compute it yourself. Check that sqft is present first: 10 new and 10 resale homes had none.

What I would do next

Two things would sharpen this. First, run the same comparison on /properties-sold to see whether the gap survives at closing, since asking prices on builder listings and on MLS resale listings may not move the same way. Second, swap the ZIP code for a radius search with lat, lon and radius_miles around each new-home community, which compares a new home with the resale homes a buyer would actually tour instead of with a postal boundary.

Every parameter and field in this post, plus the rest of its 37 endpoints, is listed in the Happy Endpoint reference for this API. Keys come from its RapidAPI subscription page; you can try each call in a browser there before writing any code. If you need this pull for every metro on a schedule, get in touch.

Top comments (0)