Blog / Web data by industry

Rental Listing Data for Market Analysis: Fields That Matter

Five fields carry most rental market analysis: asking rent with its period, bedroom count, fees and deposit, the availability date, and the listing's age or last update. US portals like HotPads give rent as a range per building, while UK portals like SpareRoom give it per room and per period. Normalise those differences before you compare anything.

Which rental fields matter for market analysis?

Start from the question. Yield work needs rent and a way to compare units of different size. Rent-index work needs the same unit measured repeatedly over time. Both need to know what the advertised rent includes and when the home can actually be occupied. That points to five field groups: rent with its period, bedroom count (and area where available), fees and deposit, availability date, and listing age.

Everything else is context. Photos, promotional labels and advertiser verification flags help a renter choose, not an analyst measure a market. Keep them out of your warehouse table unless a later question needs them, because every extra column is one more thing to validate.

The table maps each group to the fields in the two recorded responses below.

What you need HotPads (US) SpareRoom (UK)
Rent price.min, price.max, price.currency rent.min, rent.period, rent.currency, rooms[].price
Size beds.min/max, baths, sqft.min/max accommodation_type, rooms_in_property, double_rooms
Fees and deposit none in search results security_deposit, bills_included
Availability none in search results availability.from, availability.from_iso
Age and change listed_at, updated_at, price_drop_count expires_at, promotion.label
Concessions special_offer, labels none recorded
Location address, location.latitude/longitude location.postcode, location.neighbourhood

How does US portal data differ from UK portal data?

US portals list buildings with several floor plans, so the rent is a range. UK portals such as SpareRoom list a single room or a whole flat, so the rent is one number with a period. The same word, "listing", means a building in one market and a rentable unit in the other. If you merge them without noticing, your averages mix unit types.

HotPads: ranges per building

This is a trimmed excerpt of a recorded GET /v1/hotpads/search response for area=albuquerque-nm&type=apartments:

{
  "name": "Markana Uptown",
  "price": { "min": 1732, "max": 2529, "currency": "USD" },
  "beds": { "min": 0, "max": 2 },
  "sqft": { "min": 589, "max": 1022 },
  "unit_count": 12,
  "price_drop_count": 2,
  "labels": ["New Today", "Special Offer"],
  "listed_at": "2023-10-25T14:34:52Z",
  "updated_at": "2026-10-22T00:55:05Z"
  …
}

One row covers studios to two-bedrooms. A rent of 1,732 to 2,529 USD is not a rent for a two-bed. Don't compute a rent per square foot by dividing the minimum rent by the minimum area unless you know the cheapest unit is also the smallest. The GET /v1/hotpads/listing endpoint takes the listing id for a closer look.

SpareRoom: one room, one period

This excerpt comes from a recorded GET /v1/spareroom/search response for where=London:

{
  "accommodation_type": "Double room",
  "rent": { "min": 950, "period": "pcm", "rent_options": "room", "currency": "GBP" },
  "rooms": [{ "type": "double", "price": 950, "period": "pcm", "security_deposit": 950 … }],
  "bills_included": "yes",
  "security_deposit": 950,
  "availability": { "from": "2026-12-03" … }
  …
}

The other recorded row is a whole one-bed flat at 2,500 pcm with rent_options set to whole. The two rows are not comparable until you separate whole-property rents from room rents. Use rent_options for that split, and bills_included to avoid comparing a rent with utilities to one without.

Why do fees, deposits and availability dates change the numbers?

Asking rent is the start of the cost, not the end. A deposit sets the upfront cash a tenant needs, bills inclusion shifts running cost, and the availability date tells you whether a listing is on the market now or in six weeks. In the recorded SpareRoom rows, both deposits equal one month's rent (2,500 on 2,500 and 950 on 950), which is a ratio you can track across areas.

Availability matters for rent indexes. A room advertised on 2026-10-24 for a 2026-12-03 move-in is 40 days out, and the flat available 2026-11-23 is 30 days out. One hypothesis worth testing is that rents advertised for far-future dates differ from near-term ones, because the landlord is pricing a market that hasn't happened yet. The recording is too small to show it, so group listings by days-to-availability and check before you average them.

For the US, the search response in the recording carries no fee or availability fields. Zumper exposes listing-fees and building-fees endpoints, and address-rent-history and price-trends for change over time. The catalogue lists no documented parameters for them, and there's no recorded response in this article, so check the Zumper API page and run one in the playground to see which fields your target cities return.

How do you turn listings into a yield or rent-index input?

Gross yield is annual rent divided by purchase price, and the listing gives you only the numerator. You join rent to a price from a sales source, so the quality of the join (address, postcode, area) matters more than any single rent field. For the join mechanics see matching listings without IDs, and for the sales side see real estate listing data for valuation.

For an index, hold the unit type constant and track the median asking rent per cell (area, bedrooms, type) over time. Official statistics take a different route. The ONS Price Index of Private Rents uses a hedonic regression double imputation approach, covers new and existing tenancies, and draws on administrative data collected by rent officers (ONS, 2025). In the US, the BLS Housing Survey collects rent from each sampled unit every 6 months, and most rents in the sample are continuing rents (BLS, 2026). Listing data measures the advertised end of the market, so treat it as a leading signal, not a replacement.

Worked example: normalise SpareRoom rents to a weekly figure

rent.period can be pcm or pw, so convert before you compare. This script pulls one page and prints weekly rent, deposit as a share of monthly rent, and days until the room is free:

import os
from datetime import date

import requests

resp = requests.get(
    "https://api.perceptlayer.com/v1/spareroom/search",
    params={"where": "London"},
    headers={"X-API-Key": os.environ["API_KEY"]},
    timeout=30,
)
resp.raise_for_status()

for r in resp.json()["data"]["results"]:
    rent, period = r["rent"]["min"], r["rent"]["period"]
    weekly = rent * 12 / 52 if period == "pcm" else rent
    monthly = rent if period == "pcm" else rent * 52 / 12
    deposit = r.get("security_deposit")
    start = date.fromisoformat(r["availability"]["from"])
    print(
        r["id"], r["rent"]["rent_options"], round(weekly, 2),
        round(deposit / monthly, 2) if deposit else None,
        (start - date.today()).days,
    )

On the two recorded rows, the whole flat works out to 576.92 a week (2,500 × 12 / 52) and the room to 219.23 (950 × 12 / 52). Both have a deposit ratio of 1.0. Run on 2026-10-24, the days-to-availability values are 30 and 40.

To turn those rows into an index input, group the weekly rents by cell (postcode area, rent_options) and take the median per cell on each fetch date. Store the fetch date with it.

from collections import defaultdict
from statistics import median

cells = defaultdict(list)
for r in results:  # the same list as above
    rent, period = r["rent"]["min"], r["rent"]["period"]
    weekly = rent * 12 / 52 if period == "pcm" else rent
    cells[(r["location"]["postcode"], r["rent"]["rent_options"])].append(weekly)

index_row = {cell: round(median(v), 2) for cell, v in cells.items()}

With only two recorded rows, each cell holds one value (E14 whole at 576.92, N16 room at 219.23), so the median is just that rent. On a full 1,000-row sweep, the cells fill up and the median stops moving with one outlier. Yield then needs a sale price joined to the same cell: annual rent (median weekly × 52) divided by the median price.

How many calls does a market sweep take, and what does it cost?

Each call costs 1 credit, and failed calls cost nothing. The limit to plan around is reach, not price. The recorded HotPads response shows total_results of 3,541 but total_pages of 10 at 40 per page, so one query reaches 400 rows. The recorded SpareRoom response shows total_pool of 12,891, rows_reachable of 1,000 and truncated set to true.

Split the market into cells so each fits under the cap. HotPads search takes type, beds, min_price, max_price and page. SpareRoom search takes min_rent, max_rent, rent_period, room_types and page, among others.

curl "https://api.perceptlayer.com/v1/hotpads/search?area=albuquerque-nm&type=apartments&beds=2&min_price=1500&max_price=2000&page=1" \
  -H "X-API-Key: $API_KEY"

If you slice one US city by 4 bedroom counts and pull all 10 pages for each, that's 4 × 10 × 1 credit = 40 credits for a snapshot. Repeat it weekly for a year and you spend 52 × 40 = 2,080 credits. The free tier gives 1,000 credits a month after you verify an email, and pricing has what larger packs cost. Details on how calls are billed are in credits, and a sweep that runs concurrently should respect the rate limits.

When is this data the wrong tool?

Listing data shows asking rents of homes currently advertised. It doesn't show what tenants signed for, how long a home stayed empty, or the stock that never goes to market. For regulated reporting or published rent statistics, use the official series such as the ONS and BLS measures above, and use listings for timeliness and neighbourhood detail. Data is returned as found at the time of the call, so store the fetch date with every row.

To start, get a free API key, then run GET /v1/hotpads/search or GET /v1/spareroom/search from the HotPads API or SpareRoom API pages. For the same method on another asset class, see used car listing data for price analysis.

APIs in this post

Frequently asked questions

Which single field matters most for rent analysis?

Asking rent, but only with its period and unit type attached. A bare number is ambiguous: HotPads gives a min/max range per building, and SpareRoom gives rent.min with pcm or pw and a rent_options value of room or whole. Normalise to one period and one unit type before averaging.

Do HotPads search results include fees or availability dates?

The recorded search response includes price_drop_count, special_offer, listed_at and updated_at, but no fee or availability fields. For fees on US listings, check the Zumper listing-fees and building-fees endpoints on the Zumper API page, and test them in the playground.

How do I get more than the first page of results?

Add the page parameter, and narrow the query so it fits the cap. In the recordings, HotPads reports 10 pages of 40 and SpareRoom reports 1,000 reachable rows. Split by beds, min_price and max_price on HotPads, or min_rent and max_rent on SpareRoom.

What happens if a call fails?

A failed call costs nothing and returns X-Credits-Used: 0. Errors come back as an HTTP status with a JSON body containing a code, message and request_id. A rate-limited (429) response carries a Retry-After header with the seconds to wait. The errors page lists every code.

  1. Real Estate Listing Data: Fields That Matter for Valuation

    Which fields in a Zillow search response feed a valuation model (price, livingArea, yearBuilt), which are noisy, and what listing data can't tell you.

  2. Job Posting Data for Labour-Market Research

    Which Indeed posting fields support trend analysis, where postings mislead versus hires, and a weekly collection plan that costs 3,000 credits a month.

  3. Salary Data from Job Sites: Reading Ranges Without Bias

    Glassdoor's salaries endpoint returns p10 to p90 total pay, a currency and a national count. How to read those ranges and the biases of self-reported pay.

Start with 1,000 free credits

No password to set. We email you a sign-in link, and your key works on every API.

No credit card. 1,000 free credits a month.