---
title: "Rental Listing Data for Market Analysis: Fields That Matter"
description: "Which rental listing fields drive yield and rent-index work: rent range, beds, fees, deposit and availability date, with real HotPads and SpareRoom output."
url: https://perceptlayer.com/blog/rental-listing-data-market-analysis
---

[Blog](https://perceptlayer.com/blog) / [Web data by industry](https://perceptlayer.com/blog/category/web-data)

# Rental Listing Data for Market Analysis: Fields That Matter

9 Sept 2026 Perceptlayer 7 min read

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.

## Key takeaways

-   Rent, beds, fees and deposit, availability date and listing age are the fields that decide yield and rent-index work. Photos, labels and advertiser details rarely matter.
-   HotPads returns `price`, `beds`, `baths` and `sqft` as `min`/ `max` ranges per building. SpareRoom returns `rent.min` with a `period` ( `pcm` or `pw`) and a per-room `rooms` array.
-   Asking rent isn't paid rent. Track `price_drop_count`, `special_offer` and `updated_at` so concessions don't hide inside your averages.
-   One search query reaches only part of a market: 10 pages of 40 on HotPads, 1,000 rows on SpareRoom. Split by price band and bedroom count.
-   Each call costs 1 credit, and failed calls cost nothing. The worked example below prices one US city snapshot at 40 credits, or 2,080 credits for a year of weekly snapshots.
-   US fee fields are unverified here. The recorded HotPads search has no fees or availability dates, and there is no recorded Zumper response, so test the Zumper fee endpoints in the playground before you rely on them.

## 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`:

```json
{
  "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`:

```json
{
  "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](https://perceptlayer.com/apis/zumper) 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](https://perceptlayer.com/blog/deduplicate-listings-across-sources), and for the sales side see [real estate listing data for valuation](https://perceptlayer.com/blog/real-estate-listing-data-valuation-fields).

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](https://www.ons.gov.uk/economy/inflationandpriceindices/methodologies/priceindexofprivaterentsdetailedmethodology), 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](https://www.bls.gov/cpi/factsheets/owners-equivalent-rent-and-rent.htm), 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:

```python
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.

```python
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.

```bash
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](https://perceptlayer.com/pricing) has what larger packs cost. Details on how calls are billed are in [credits](https://docs.perceptlayer.com/credits), and a sweep that runs concurrently should respect the [rate limits](https://docs.perceptlayer.com/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](https://perceptlayer.com/signup), then run `GET /v1/hotpads/search` or `GET /v1/spareroom/search` from the [HotPads API](https://perceptlayer.com/apis/hotpads) or [SpareRoom API](https://perceptlayer.com/apis/spareroom) pages. For the same method on another asset class, see [used car listing data for price analysis](https://perceptlayer.com/blog/used-car-listing-data-price-analysis).

## APIs in this post

-   [Zumper **1 credit per call** · 50 endpoints](https://perceptlayer.com/apis/zumper)
-   [HotPads **1 credit per call** · 2 endpoints](https://perceptlayer.com/apis/hotpads)
-   [SpareRoom **1 credit per call** · 4 endpoints](https://perceptlayer.com/apis/spareroom)

## 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](https://perceptlayer.com/apis/zumper), 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](https://docs.perceptlayer.com/errors) lists every code.

rental data real estate rent index rental yield hotpads spareroom

## More from the blog

1.  [Web data by industry](https://perceptlayer.com/blog/category/web-data) 12 Jul 2026 Perceptlayer 10 min read
    
    ## [Real Estate Listing Data: Fields That Matter for Valuation](https://perceptlayer.com/blog/real-estate-listing-data-valuation-fields)
    
    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.  [Web data by industry](https://perceptlayer.com/blog/category/web-data) 21 Oct 2026 Perceptlayer 8 min read
    
    ## [Job Posting Data for Labour-Market Research](https://perceptlayer.com/blog/job-posting-data-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.  [Web data by industry](https://perceptlayer.com/blog/category/web-data) 6 Oct 2026 Perceptlayer 7 min read
    
    ## [Salary Data from Job Sites: Reading Ranges Without Bias](https://perceptlayer.com/blog/salary-data-job-sites-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.
