DEV Community

Cover image for Scrape Rent.com Data with Python: Is the Listed Rent Really What You Pay?
Happy Endpoint
Happy Endpoint

Posted on Originally published at Medium

Scrape Rent.com Data with Python: Is the Listed Rent Really What You Pay?

A one-bedroom in Atlanta lists at $1,464 a month on Rent.com. Open the cost breakdown and the estimated monthly total is $1,552: trash, pest control, a resident services fee, parking and a utility billing fee add $88 before you pay for power or water. Elsewhere in Atlanta, a two-bedroom lists at $1,824, and its breakdown shows a base rent of $1,627 plus $197 of fees. Same city, same site, opposite conventions.

So is the rent on a listing what you actually pay? I scraped Rent.com data with Python for 40 apartment communities in five US cities and read the cost estimate behind each one. The short answer: 22 of the 40 charge required monthly fees, a median of $45 a month. In 10 of those the listed rent already includes them. In 9 it does not. And 15 of the 40 list required utilities with no price at all.

The data

Everything here comes from the Rent.com API, which returns Rent.com search results and full listings as JSON. On 4 November 2026 I ran one /search per city (Austin, Atlanta, Phoenix, Denver and Chicago) with property_types=apartments and page_size=500, in Rent.com's default order. That gave 2,500 apartment rows.

Search rows carry the listed rent but not the fees. Those are on /listing-details, in a cost_estimate object with four parts: monthly_fees (required and optional lines), move_in_fees, usage_based_fees and a monthly_total. So I sorted each city's 500 rows by price and picked 8 at evenly spaced points, from the cheap end to the expensive end, then fetched the details for those 40. That is 45 successful calls in all.

Forty listings is a sample for reading the structure of the fees, not a census. City by city it is 8 listings each, so treat the city numbers as examples. Every one of the 40 came with a cost estimate.

The code

This is the core of it: search a city, open each listing, add up the required monthly lines that carry a price, and compare the result with the listed rent. It runs as printed with requests once RAPIDAPI_KEY is set.

import json
import os
import time

import requests

HOST = "rent-com1.p.rapidapi.com"  # Rent.com API on RapidAPI
BASE = os.environ.get("API_BASE", "https://" + HOST)


def headers():
    if "API_BASE" in os.environ:
        return json.loads(os.environ.get("API_HEADERS", "{}"))
    return {"x-rapidapi-host": HOST, "x-rapidapi-key": os.environ["RAPIDAPI_KEY"]}


def get(path, **params):
    r = requests.get(BASE + path, params=params, headers=headers(), timeout=120)
    r.raise_for_status()
    time.sleep(1.5)
    return r.json()["data"]


def charge(line):
    """Lowest amount of one fee line; None when Rent.com prints 'Varies'."""
    if line["min"] is None:
        return None
    return line["min"] * ((line.get("quantity") or {}).get("min") or 1)


def monthly_cost(listing_id, listed_rent):
    estimate = get("/listing-details", listing_id=listing_id)["cost_estimate"]
    required = estimate["monthly_fees"]["required"]
    base = next(x["min"] for x in required if x["category"] == "BASE_RENT")
    extras = [x for x in required if x["category"] != "BASE_RENT"]
    fees = sum(charge(x) for x in extras if charge(x) is not None)
    varies = [x["name"] for x in extras if charge(x) is None]
    total = estimate["monthly_total"]["min"]
    if not fees:
        where = "no fixed fees"
    elif abs(listed_rent - total) < abs(listed_rent - base):
        where = "fees already in the listed rent"
    else:
        where = "fees on top of the listed rent"
    return base, fees, total, where, varies


rows = get("/search", location_slug="georgia/atlanta", property_types="apartments", page_size=50)["listings"]
for row in [x for x in rows if x["listing_id"].startswith(("lc", "lv"))][:3]:
    base, fees, total, where, varies = monthly_cost(row["listing_id"], row["price"])
    print("%s listed $%s | base $%s + fees $%.2f = $%.2f | %s" % (
        row["listing_id"], row["price"], base, fees, total, where))
    if varies:
        print("    priced as 'Varies':", ", ".join(varies))
Enter fullscreen mode Exit fullscreen mode

When I ran it, the first three Atlanta communities in the default order all came back clean:

lc6031960 listed $1145 | base $1145 + fees $0.00 = $1145.00 | no fixed fees
lc6178142 listed $1050 | base $1050 + fees $0.00 = $1050.00 | no fixed fees
lc7007425 listed $1028 | base $1028 + fees $0.00 = $1028.00 | no fixed fees
Enter fullscreen mode Exit fullscreen mode

In my sample, 18 of the 40 listings had no required monthly fee with a price on it. The other 22 are where it gets interesting.

My full analysis does one thing more than this script. Instead of comparing the listing's cheapest price with its cheapest total, it lines up every unit: floor_plans[].units[].rent against the same unit's row in cost_estimate.by_unit, which has its own base rent and total. A listing counts as "in the listed rent" when the unit rents match the unit totals within 1%, and "on top" when they match the base rents.

What the fees are

Trash, utilities and pest control are the usual add-ons

The required monthly lines are mostly small and mostly about building services. Trash shows up on 17 of the 40 listings (often "valet trash", a doorstep pickup), at a median $28.50 where it has a price. A utility billing fee, the charge for passing the utility bills on to you, shows up on 13, at a median $5.80. Pest control appears on 9 at a median $6. Internet, cable or "smart tech" packages are rarer, 5 listings, but expensive: a median $75 a month.

Added up, the 22 listings with priced required fees charge a median of $45 a month, or about $543 a year. That is 3.2% of the base rent at the median. The spread is wide: 8 of the 22 charge under $25, and 6 charge $100 or more. The largest was the Atlanta community with the $1,824 two-bedroom: $197 a month in fees, 17.6% of its lowest base rent of $1,120, made of internet ($90), doorstep trash ($35), a washer and dryer rental ($35), package lockers ($24), a utility billing fee ($7) and pest control ($6).

Are the fees inside the listed rent or on top of it?

Fees sit inside the listed rent about as often as on top

This is the part that trips up anyone comparing rents across listings. Of the 22 listings with fixed fees:

  • 10 already include them in the listed rent. The listed unit rent equals Rent.com's estimated monthly total, and the BASE_RENT line in the breakdown is lower. For the Atlanta two-bedroom, unit 1010 lists at $1,824; the breakdown says $1,627 base rent plus fees, total $1,824.
  • 9 add them on top. The listed rent is the base rent, and the total is higher. The Atlanta one-bedroom from the opening is one: $1,464 listed, $1,552 total. The median add-on in this group was $50 a month, or 4.5% of the rent.
  • 3 match neither. In one, the estimate prices a different lease length from the listed rent; in the other two, a fee comes as a range ($25 to $51 for water, $65 to $125 for amenities).

So adding the fees to the listed price every time double-counts them in about half of the cases, and never adding them understates the other half. If you compare apartments, compare monthly_total per unit, not price.

The city split is where it gets suggestive. In Denver, 6 of the 7 listings with fees had them inside the listed rent. Colorado's HB25-1090, in force since 1 January 2026, requires anyone advertising a price to show the total price, and lets landlords leave out the actual amount charged for utilities. That is exactly the shape of the Denver data: the small fixed fees sit inside the listed rent, and the water, sewer, power and trash reimbursements are listed as "Varies" in 5 of the 8 listings. Eight listings cannot prove a cause, but the pattern fits the law closely. In Chicago, all 3 listings with fees put them on top.

City Listings With fixed fees Median fees where charged Fees in listed rent Fees on top With 'Varies' lines
Austin 8 3 $110.60 2 0 2
Atlanta 8 7 $88.00 2 3 5
Phoenix 8 2 $114.50 0 2 2
Denver 8 7 $5.50 6 1 5
Chicago 8 3 $15.00 0 3 1

The fees nobody priced

A fee line in monthly_fees.required can have min and max set to null and text set to "Varies". Those lines are required, but they are not in monthly_total, because there is nothing to add. 15 of the 40 listings had at least one, and 14 of those 15 were utilities: electric, gas, water, sewer, sometimes billed back to you as a share of the building's bill. Four listings also listed renters insurance this way, a requirement with the price left to you.

This matters more than the small fixed fees. A $4.16 utility billing fee in Denver is a rounding error; the electricity and water it bills for are not. Rent.com's estimate cannot tell you that number, and neither can any scraper of it. Count the "Varies" lines and flag the listing rather than treating it as fee-free.

What you pay before you move in

The admin fee is more than five times the application fee

27 of the 40 listings had a move-in estimate. Its median move_in_total was $601, but that figure mixes refundable deposits with fees you never see again. Taking only application fees, admin fees and the small one-time charges (key fobs, utility account set-up), the median was $265.

Application fees are the familiar part: 24 listings, a median $50 and a maximum of $150. The admin fee is the bigger one: 15 listings charge one, at a median $275 and up to $500, and 4 of them charge it per person, so two roommates pay it twice. Two more listings show an admin fee of exactly $0, which tells you the field exists even when it is waived. Where a security deposit is listed it is a median $500, and 8 listings also take a holding deposit or holding fee, a median $165.

Pets: rent, fees and deposits

36 of the 40 accept dogs and the same number accept cats. 26 charge monthly pet rent, a median $25 and at most $50. 21 charge a one-time pet fee, a median $375 and up to $500. For one pet, a year of pet rent plus the up-front fee comes to a median $630, from $220 to $1,100 depending on the building.

Denver again looks different. All 6 Denver listings with pet rent charge exactly $35, none charges a one-time pet fee, and the 6 pet deposits run from $150 to $300. Colorado's HB23-1068 caps a pet deposit at $300 and pet rent at $35 or 1.5% of the rent, whichever is greater, and the data sits right on those caps. Atlanta, by contrast, had one-time pet fees of $350 to $500 on 7 of 8 listings.

Pitfalls in the Rent.com data

The fees object is mostly empty. Every listing has a fees block with application_fee, admin_fee and broker_fee, but only 9 of the 40 had anything in it. The real numbers are in cost_estimate. Read that.

Quantities hide per-person charges. A line such as "Pest Control Fee ($5 each)" comes with quantity from 1 to 4. I counted the minimum, one. With roommates or a second pet the real charge is higher, and the same goes for "per person" admin fees.

"Required" is not always required. One Denver listing puts pet rent of $35 in its required monthly lines, so its monthly_total assumes you have a pet. Two listings put a pet charge in their required move-in fees. Check line categories (PET_RENT, CAT_FEE, DOG_FEE) before you sum.

Listed rent and base rent are different fields. On 25 of the 40 listings price equals the lowest BASE_RENT. On the rest it does not, either because fees are inside the listed rent or because the estimate uses another unit or lease term. Never subtract one from the other across different units.

Some search ids do not open. 50 of the 2,500 search rows had plain numeric ids instead of lc... or lv.... Both /listing-details and /listing-by-url returned 400 for the one I tried, so my sampler skips them and takes the next listing up. Filter on the prefix before fetching details.

Fee names are free text. I saw "Trash", "Valet Trash ($35 each)", "Trash Services - Doorstep" and "Trash reimbursement" across four buildings. Group on category first (GARBAGE_FEE, VALET_TRASH_FEE, PEST_CONTROL_FEE) and fall back to the name only for CUSTOM lines, which were common.

Leasing contacts are in the payload. Listings carry leasing office phone numbers and contact fields. Drop them before you store anything, as I did for this dataset.

Next steps

I would put this on a weekly schedule to see whether more buildings fold their fees into the listed rent over time, and pull every Denver apartment to check the Colorado pattern on more than eight listings.

You can get a key for the Rent.com API on RapidAPI. To try each call before writing any code, open the Postman docs; for Python and JavaScript starters, see the rent-com-api repo on GitHub. My LinkedIn overview of the Rent.com API covers the rest of what it returns, such as rents by bedroom and rent estimates.

Want US rental data delivered as a file instead? We can build a custom dataset for you.

Top comments (0)