# SpareRoom API & Scraper - UK Rooms to Rent & New Room Monitor (`neverempty/spareroom-listings-monitor`) Actor

For landlords, letting agents and rent research: UK rooms to rent and flatshares from SpareRoom as an API, by city or area. Rent in GBP with pw/pcm and a monthly figure, bills, room type, available date, postcode district, agent or landlord. Monitoring returns only new rooms and rent drops.

- **URL**: https://apify.com/neverempty/spareroom-listings-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Real estate, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 listing returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SpareRoom API & Scraper - UK Rooms to Rent & New Room Monitor

Pull UK **rooms to rent** from **SpareRoom (spareroom.co.uk)** — the UK's largest flatshare and room-rental site — as clean JSON: the rent in GBP as a number with its period (**pw** or **pcm**) and a monthly equivalent, bills included, room type (double, single, whole flat), rooms available, available date, area and **postcode district**, property type, rooms in the property, and whether the advertiser is a **letting agent, landlord or flatmate**. Search by **town or city** (London, Manchester, Bristol, Edinburgh ...) or by an **area inside it** (Canning Town, Shoreditch, Didsbury ...).

Turn on monitoring and every later run returns only **new rooms and rent drops** since the last run, with the previous rent.

*Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by SpareRoom or Flatshare Ltd. All product names are trademarks of their respective owners.*

No API key, no login. Reads the same public list pages a visitor sees, at a gentle pace, and checks spareroom.co.uk/robots.txt on every run.

***

### Who uses this

- **Landlords and letting agents** pricing a room against what is advertised on the same street right now.
- **Rent and housing analysts** tracking room rents by city, area and postcode district, week by week.
- **Relocation services and tenants' tools** watching an area for new rooms and price cuts.

### Monitoring: run it daily, get only what changed

Set `monitoringMode: true` and schedule the Actor:

1. **First run** — every listing on the pages read is remembered with its rent. Nothing is returned and nothing is charged.
2. **Every later run** — only rooms that are **new** (`changeType: "new"`) or **cheaper than last time** (`changeType: "rent-drop"`, with `previousRentPcmEquivalent`) are returned. Weekly and monthly rents are compared on the same monthly scale, so an advert that switches from £200 pw to £867 pcm is not reported as a drop.
3. Every page compared that returned no change is charged one **search-checked** event (one per page of 10 listings, not per listing). A run where nothing changed returns one free note row plus those page checks; a run that returns changes is charged for the rows plus the pages that held no change. Narrow the search (an area instead of the whole city) or lower `maxPages` to compare fewer pages.
4. Rows you could not receive because your maximum charge was reached are **not** remembered, so they come back on the next run.

How "new" works: SpareRoom lists rooms newest and most recently boosted first, and shows at most 100 pages (1,000 listings) per search. Monitoring compares the first pages of that order (30 pages by default). "New" means the listing was not on the pages read last time — usually a freshly placed advert; an older advert that its owner boosted back to the top also counts, and `adStatus` (`new today`, `new`, `boosted`) tells you which.

### What makes the data usable

- **Rent as numbers**: `rentGbp` (lowest room), `rentMaxGbp` (highest room when the advert offers several), `rentPeriod` (`pw` / `pcm`) and `rentPcmEquivalent` (weekly x 52 / 12, the usual UK conversion). The text SpareRoom shows is kept in `rentText`.
- **Agent or private**: `advertiserType` is `agent`, `live-out-landlord`, `live-in-landlord`, `current-flatmate` or `former-flatmate`, and `isAgent` is a plain true/false. The advertiser's name, photo and profile are **never** returned.
- **Room type you can filter on**: `roomType` is `double`, `single`, `mixed` (single and double rooms in one advert) or `whole-property`, with `roomsAvailable`.
- **Honest nulls**: a value SpareRoom does not show on the list page stays `null` — it is never guessed. `billsIncluded` is `true` when SpareRoom marks the rent as bills included and `null` otherwise (the list page does not say "bills excluded").
- Phone numbers and e-mail addresses written in an advert's summary are replaced with `[phone removed]` / `[email removed]`.

### Input

| Field | Example | Notes |
|---|---|---|
| `city` | `"Manchester"` | UK town or city as SpareRoom spells it |
| `area` | `"Didsbury"` | Optional area inside the city |
| `searchUrl` | `"https://www.spareroom.co.uk/flatshare/london/canning_town"` | Optional; a SpareRoom list page. Overrides city and area |
| `maxListings` | `20` | Rows to return (1-1,000) |
| `maxPages` | `0` | Page cap (SpareRoom stops at 100). 0 = from maxListings; monitoring 30 |
| `minRentPcm` / `maxRentPcm` | `600` / `1000` | Monthly rent in GBP; weekly rents are converted |
| `roomTypes` | `["double"]` | `double`, `single`, `whole-property` |
| `excludeAgents` | `true` | Keep landlords and flatmates only |
| `billsIncludedOnly` | `true` | Keep only bills-included rooms |
| `keywords` | `["ensuite", "E14"]` | Match in title, summary, area or postcode district |
| `monitoringMode` | `true` | New rooms and rent drops only (see above) |
| `resetMonitoringState` | `false` | Forget the memory for this search |

Filters are applied to the listings read (SpareRoom's own filter parameters are disallowed for crawlers in its robots.txt), so a narrow filter may need more pages; rows that are filtered out are free.

```json
{ "city": "London", "area": "Canning Town", "maxListings": 50, "roomTypes": ["double"], "maxRentPcm": 1100 }
```

### Output

One row per room advert. Example (values made up for illustration):

```json
{
  "source": "spareroom.co.uk",
  "status": "ok",
  "listingId": "18400001",
  "url": "https://www.spareroom.co.uk/flatshare/manchester/didsbury/18400001",
  "title": "Bright double room near the tram stop",
  "area": "Didsbury",
  "postcodeDistrict": "M20",
  "rentText": "£650 pcm",
  "rentGbp": 650,
  "rentMaxGbp": 650,
  "rentPeriod": "pcm",
  "rentPcmEquivalent": 650,
  "billsIncluded": true,
  "roomsOfferedText": "Double room",
  "roomType": "double",
  "roomsAvailable": 1,
  "wholePropertyBedrooms": null,
  "ensuiteMentioned": null,
  "availableFrom": "2026-11-01",
  "availableNow": false,
  "propertyType": "house",
  "roomsInProperty": 4,
  "advertiserType": "live-out-landlord",
  "isAgent": false,
  "adPlacement": "bold",
  "adStatus": "new today",
  "verifiedAdvertiser": true,
  "earlyBird": false,
  "mondayToFridayLet": false,
  "photoCount": 8,
  "hasVideo": false,
  "summary": "Double room in a quiet shared house, five minutes from the shops.",
  "imageUrl": "https://photos2.spareroom.co.uk/images/flatshare/listings/unmodified/00/00/0000000.jpg",
  "searchedLocation": "Didsbury, Manchester",
  "searchUrl": "https://www.spareroom.co.uk/flatshare/manchester/didsbury",
  "totalOnSpareRoom": "248",
  "changeType": null,
  "previousRentPcmEquivalent": null,
  "scrapedAt": "2026-10-09T09:00:00.000Z"
}
```

Measured on 193 adverts from SpareRoom list pages read on 2026-10-09 (London, Manchester, Canning Town, Bath, Truro, Edinburgh): a numeric rent with its period on 193/193 (26 weekly), room type on 193/193, advertiser type on 193/193 (50 agents), postcode district on 193/193, an available date or "available now" on 193/193, bills marked included on 131/193.

`ensuiteMentioned` is `true` when the title or summary mentions an en-suite and `null` otherwise — SpareRoom's list page has no en-suite field. The minimum and maximum tenancy term are only on the individual advert page, which robots.txt disallows, so they are not returned.

#### Rows that are never charged

| `status` | Meaning |
|---|---|
| `no-results` | SpareRoom answered and said 0 results for this search |
| `no-such-location` | SpareRoom does not know this town or area (it sends the request to its search form) — not a claim that there are no rooms |
| `no-filter-match` | Listings were read but your filters removed all of them |
| `no-new-listings` | Monitoring: nothing new and no rent drop since the last run |
| `monitoring-started` | Monitoring: first run, listings remembered |
| `unreadable` / `blocked` | A page could not be read or SpareRoom did not serve it; nothing is guessed and no check is solved |
| `disallowed-by-robots` | The address is disallowed in spareroom.co.uk/robots.txt and was not requested |

### Pricing

Pay per event: you pay for each listing row returned (`listing-returned`). In monitoring mode only, each list page that was compared with the remembered listings and held no new room or rent drop is charged one `search-checked` event. Pages are not charged outside monitoring mode, the first monitoring run (which only remembers) is free, runs where a page could not be read charge no checks, and every note row above is free. If your maximum charge is reached, rows come first and no checks are charged beyond the limit. See the Pricing tab for the current prices.

### Notes

- SpareRoom shows 10 adverts per page plus one featured advert, and at most 100 pages per search. For a big city (London has over 12,000 room adverts) search by area to reach further.
- Featured adverts repeat across pages; each advert is returned once per run.
- Only "rooms offered" adverts are read, not "rooms wanted" profiles.
- Individual advert pages are not opened (robots.txt disallows them); every field comes from the list page.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `city` (type: `string`):

The town or city to search on SpareRoom UK, written as SpareRoom spells it: London, Manchester, Bristol, Edinburgh, Milton Keynes. Required unless a SpareRoom list URL is given. An unknown name returns a free row saying SpareRoom does not know the place.

## `area` (type: `string`):

An area SpareRoom lists inside the city, e.g. Canning Town or Shoreditch (London), Didsbury (Manchester). Leave empty to search the whole city.

## `searchUrl` (type: `string`):

Paste a SpareRoom rooms-to-rent list page, e.g. https://www.spareroom.co.uk/flatshare/london or https://www.spareroom.co.uk/flatshare/london/canning_town. When set, city and area are ignored. Leave empty to use the fields above.

## `maxListings` (type: `integer`):

How many room listings to return. You are charged only for rows you receive. SpareRoom shows 10 listings per page and at most 1,000 per search; empty = 20.

## `maxPages` (type: `integer`):

Hard cap on list pages requested (SpareRoom stops at page 100). 0 or empty = decided from maxListings (monitoring: 30 pages). Outside monitoring, reading pages is free and only returned listings are charged; in monitoring mode each page compared without a change is charged one search-checked event.

## `minRentPcm` (type: `integer`):

Keep rooms whose rent is at least this many pounds per calendar month. Weekly rents are converted (x 52 / 12) before comparing. 0 or empty = no minimum.

## `maxRentPcm` (type: `integer`):

Keep rooms whose rent is at most this many pounds per calendar month (for a price range, the lowest room counts). Weekly rents are converted (x 52 / 12). 0 or empty = no maximum.

## `roomTypes` (type: `array`):

Keep only these room types: double, single, whole-property (a whole flat or house offered). A listing offering both single and double rooms matches single or double. Listings whose room type cannot be read are dropped when this is set. Empty = all types.

## `excludeAgents` (type: `boolean`):

On: drop listings placed by letting agents and keep landlords and flatmates. Listings whose advertiser type cannot be read are dropped too. Off: keep everyone.

## `billsIncludedOnly` (type: `boolean`):

On: keep only listings that SpareRoom marks as bills included. Off: keep all.

## `keywords` (type: `array`):

Keep listings whose title, summary, area or postcode district contains one of these words, e.g. ensuite, garden, E14. Not case-sensitive. Empty = no keyword filter.

## `monitoringMode` (type: `boolean`):

On: the first run remembers every listing on the pages read and its rent (free); every later run returns only rooms that are new or cheaper since the last run, with changeType and previousRentPcmEquivalent. Off: return the current listings.

## `resetMonitoringState` (type: `boolean`):

On: forget what monitoring remembered for this search, so the next monitoring run starts again from a free baseline. Off: keep the memory.

## Actor input object example

```json
{
  "city": "Manchester",
  "maxListings": 20,
  "excludeAgents": false,
  "billsIncludedOnly": false,
  "monitoringMode": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per SpareRoom room listing: listing id and URL, title, area and postcode district, rent as SpareRoom shows it plus the rent in GBP as a number (ranges as min/max) with its period (pw or pcm) and a monthly equivalent, bills included, room type and rooms available, available date, property type, rooms in the property, advertiser type (agent, landlord or flatmate - never the name), ad placement and status. Monitoring rows add changeType (new or rent-drop) and previousRentPcmEquivalent. Unknown places, filters that matched nothing and pages that could not be read come back as their own rows and are not charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "city": "Manchester",
    "maxListings": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/spareroom-listings-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "city": "Manchester",
    "maxListings": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/spareroom-listings-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "city": "Manchester",
  "maxListings": 20
}' |
apify call neverempty/spareroom-listings-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/spareroom-listings-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/nmyviXOD4HoTuYrgv/builds/V1hUpXqmQCo1CRfuk/openapi.json
