# Rightmove Scraper & New-Listing Monitor - UK Property Data (`neverempty/rightmove-listings`) Actor

Turn monitoring on and every later run returns only the listings it has not returned before, so you are never charged for the same property twice. Every Rightmove.co.uk listing as clean JSON: price with its meaning, bedrooms, type, address with coordinates, agency. No agent phones. Unofficial.

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

## Pricing

from $0.69 / 1,000 property 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Rightmove Scraper - UK Property Data, Price & Bedrooms

Pull property listings from **Rightmove.co.uk** — the UK's largest property portal — as clean JSON: the price as a number and what it means, bedrooms, bathrooms, size, tenure, coordinates, key features and the agency, for sale or to rent, anywhere in the UK.

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

No API key. Reads the same public pages a visitor sees.

***

### Run it tomorrow and you only get what is new

Schedule this Actor with `monitoringMode: true` and each run returns **only the listings it has not returned before**. Not "here is everything again, please de-duplicate it yourself" — the Actor remembers what it already gave you, so a daily feed of a 4,000-listing search costs you the handful of new homes, not 4,000 rows every morning. **You are never charged for the same property twice.**

Listings your filters removed are remembered too, so widening a filter later does not resurrect them as false "new" listings. Listings that did not fit under your limit are **not** remembered, so they are still waiting on the next run instead of being silently lost.

***

### Why this one is different

#### "POA" is not sold to you as a price

Rightmove's page data carries a numeric `amount` on every listing — **including the ones whose displayed price is "POA"** (price on application). One of the 150 listings measured shows "POA" to visitors while carrying 65,000,000 underneath.

This Actor does not pass that number off as the price. A POA listing comes back with `price: null`, `priceDisplay: "POA"` and `isPriceOnApplication: true`. Where Rightmove shows a figure, you get it as a number plus Rightmove's own qualifier — `Guide Price`, `Offers Over`, `Offers in Excess of`, `Fixed Price` — in `priceQualifier`.

#### Rent is labelled by its real period, and both figures are Rightmove's own

A rent on Rightmove is stored in the period the agent chose: most are monthly, some are weekly (2 of the 25 rentals measured). `priceType` says which — `monthly-rent` or `weekly-rent` — and `rentPerMonth` / `rentPerWeek` are read from the two figures Rightmove itself displays ("£2,448 pcm", "£565 pw"), never converted here. Sales come back as `priceType: "sale"`.

#### Every row says where Rightmove actually searched

Rightmove resolves any place name to *some* place. `Wales` is a village in Somerset. `Scotland` is a district of Leeds. `Kings Lynn` is the railway station, with a half-mile radius. Instead of guessing, every row carries `searchedLocation` (the name Rightmove shows) and `searchedLocationId` (`REGION^87490`, `OUTCODE^2506`, `STATION^5177`), so a wrong resolution is visible in the data rather than silent.

#### "Not found" is not "no results"

Rightmove answers an unknown location — or a value it does not accept, such as a 2-mile radius — with an HTTP 200 and a redirect to its not-found page. Read naively, that is an empty search. This Actor follows the redirect, recognises the page, and returns a `no-such-search` row that says what Rightmove accepts. A search that really has zero results is a different row (`no-results`), and both are free. Values Rightmove would reject are refused up front with the allowed list, so you are never charged for a run that could only end in a redirect.

#### No branch phone numbers

Rightmove's listing data includes the marketing branch's telephone on **every one of the 150 listings measured**. This Actor returns the agency name, branch name and branch page, and leaves the phone out; UK phone numbers written into the summary or features are removed as well. Property data, not a contact list.

#### One request, 24 complete properties

Rightmove ships the full listing — price, bedrooms, bathrooms, size, tenure, coordinates, features, agency, dates — inside the search page itself. This Actor reads them from that single page and **never opens individual property pages**. The featured property that repeats across pages is dropped before delivery, so you are never charged twice for the same listing.

#### Rightmove's own filters, then yours

Price, bedrooms, property type, radius, "added or reduced within", must-haves, exclusions, furnishing and sold/let-agreed are sent to Rightmove, so the pages fetched already contain only matching properties — each of these was checked on live pages and the returned rows satisfy them. Keywords are **not** sent, because Rightmove ignores them in the URL (a nonsense keyword returned the full list); the keyword filter, along with bathrooms, size, tenure, agency and first-listed date, runs here on the rows already fetched. Nothing extra is requested and nothing extra is charged. A property is never dropped for a value Rightmove did not publish.

***

### Input

Either paste a search URL, or build one from the fields below.

| Field | Type | Default | Description |
|---|---|---|---|
| `searchUrl` | string | — | e.g. `https://www.rightmove.co.uk/property-for-sale/find.html?locationIdentifier=REGION%5E87490` or `https://www.rightmove.co.uk/property-to-rent/Manchester.html`. Price, bedroom, type, radius, must-have and sort parameters are kept (a sort in the URL wins over `sortBy`); `keywords=` is dropped |
| `channel` | string | `sale` | `sale` or `rent` |
| `location` | string | `London` | A town (`Milton Keynes`), an outcode (`SW1A`), or an identifier from a Rightmove address (`REGION^87490`) |
| `maxListings` | integer | 20 | 1–1000. One property = one row = one charged event. Rightmove itself stops at 1,008 per search |
| `maxPages` | integer | 0 | Hard cap on requests; 0 = decide from `maxListings`. Rightmove has 42 pages at most |
| `includeSummary` | boolean | `true` | The card teaser. The keyword filter searches it either way |
| `useProxy` | boolean | `false` | Off by default — Rightmove answers Apify's network directly. Switches on by itself if blocked |
| **Rightmove's own filters** | | | |
| `minPrice` / `maxPrice` | integer | 0 | GBP. Sale total, or rent per calendar month. POA listings are kept |
| `minBedrooms` / `maxBedrooms` | integer | -1 | -1 = off. 0 is a studio |
| `propertyTypes` | array | — | `detached`, `semi-detached`, `terraced`, `bungalow`, `flat`, `land`, `park-home`, `private-halls` |
| `radius` | string | `0` | Miles: `0.25`, `0.5`, `1`, `3`, `5`, `10`, `15`, `20`, `30`, `40` — Rightmove's steps |
| `sortBy` | string | `newest` | `newest` (last listed or reduced), `oldest`, `price-high`, `price-low` |
| `addedOrReducedWithinDays` | string | `0` | `1`, `3`, `7` or `14`. Rightmove counts a price reduction as an update |
| `mustHave` / `dontShow` | array | — | `garden`, `parking`, `newHome`, `retirement`, `sharedOwnership`, `auction` |
| `furnishTypes` | array | — | Rent only: `furnished`, `partFurnished`, `unfurnished` |
| `includeSSTC` / `includeLetAgreed` | boolean | `false` | Include sold-STC (sale) or let-agreed (rent) listings |
| **Filters applied here** | | | |
| `keywords` / `keywordMatch` / `excludeKeywords` | array / string / array | — | Match on title, address, summary, type, features, labels and agency |
| `agencyNames` | array | — | Keep only these agencies (partial match) |
| `requirePrice` | boolean | `false` | Drop POA listings |
| `minBathrooms` | integer | -1 | -1 = off. **An unpublished count is kept** |
| `minSizeSqFt` / `maxSizeSqFt` | integer | 0 | 0 = off. **A listing without a size is kept** |
| `tenureTypes` | array | — | `FREEHOLD`, `LEASEHOLD`, `SHARE_OF_FREEHOLD`. **An unpublished tenure is kept** |
| `excludeCommercial` / `excludeStudents` | boolean | `false` | Drop commercial or student listings that Rightmove mixes in |
| `listedWithinDays` | integer | 0 | By the date the property first appeared, ignoring reductions. 0 = off |
| `monitoringMode` | boolean | `false` | Return only properties not returned on a previous run |
| `resetMonitoringState` | boolean | `false` | One-shot: forget what was already returned |

```json
{ "channel": "sale", "location": "Milton Keynes", "maxListings": 100,
  "minBedrooms": 3, "maxPrice": 450000, "propertyTypes": ["detached", "semi-detached"],
  "addedOrReducedWithinDays": 7, "dontShow": ["sharedOwnership", "retirement"], "tenureTypes": ["FREEHOLD"] }
```

#### Monitoring mode turns this into a daily feed

Schedule it and set `monitoringMode: true`: each run returns **only the properties it has not returned before**. Properties removed by your filters are remembered too, so changing a filter later does not resurrect them as false "new" listings — and properties that simply **did not fit under `maxListings` are not remembered**, so they are still waiting for you on the next run rather than being silently lost. Combine with `addedOrReducedWithinDays: 1` to keep each run to one or two pages.

Measured on 2026-09-05: **59,361** properties for sale and **27,624** to rent in London.

***

### Output

One row per property:

```json
{
  "source": "rightmove.co.uk",
  "status": "ok",
  "listingId": "90204729",
  "title": "3 bedroom semi-detached house for sale",
  "address": "Elsiemaud Road, London",
  "url": "https://www.rightmove.co.uk/properties/90204729#/?channel=RES_BUY",
  "searchedLocation": "London",
  "searchedLocationId": "REGION^87490",
  "channelRaw": "BUY",
  "transactionType": "sale",
  "isCommercial": false,
  "propertyType": "Semi-Detached",
  "bedrooms": 3,
  "bathrooms": 2,
  "price": 950000,
  "priceCurrency": "GBP",
  "priceType": "sale",
  "priceDisplay": "£950,000",
  "priceQualifier": "Offers in Excess of",
  "isPriceOnApplication": false,
  "rentPerMonth": null,
  "rentPerWeek": null,
  "sizeSqFt": null,
  "sizeRaw": null,
  "tenure": "FREEHOLD",
  "latitude": 51.45449,
  "longitude": -0.029782,
  "features": ["Sold Chain Free", "Desired Location", "Over 1500 sq. ft", "Driveway", "Landscaped Garden", "Double Glazed", "Quiet Road", "EPC - C"],
  "labels": [],
  "isNewHome": false,
  "isDevelopment": false,
  "isBuildToRent": false,
  "isStudentListing": false,
  "isAuction": false,
  "isPremiumListing": true,
  "isFeaturedListing": true,
  "letAvailableDate": null,
  "feesText": null,
  "agencyName": "Acorn",
  "agencyBranch": "Acorn, Brockley",
  "agencyUrl": "https://www.rightmove.co.uk/estate-agents/agent/Acorn/Brockley-270020.html",
  "imagesCount": 28,
  "floorplansCount": 1,
  "virtualToursCount": 1,
  "mainImageUrl": "https://media.rightmove.co.uk:443/dir/crop/10:9-16:9/property-photo/…_max_476x317.jpeg",
  "firstListedAt": "2026-06-26T16:40:02Z",
  "lastUpdateReason": "price_reduced",
  "lastUpdatedAt": "2026-08-29T14:40:00Z",
  "summary": "Situated on a sought-after residential road in the heart of Brockley, …",
  "scrapedAt": "2026-09-05T07:08:16.112Z"
}
```

Measured across 150 live listings (149 unique) on 2026-09-05: id, title, address, url, property type, bedrooms, coordinates, agency and both dates on **150 of 150**; a displayed price on **149** (the other is POA); bathrooms on **146**; key features on **124**; tenure on **121**; a size in sq ft on **93**. `bedrooms: 0` is a real value (a studio, a plot, a commercial unit), not a missing one. The summary is Rightmove's card teaser and is cut short by Rightmove on about a third of listings.

#### Rows that are never charged

| `status` | when |
|---|---|
| `no-results` | Rightmove reports zero properties for this search. That is its answer, not a failure |
| `no-such-search` | Rightmove redirected to its not-found page: the location is not one it knows, or a value is not one it accepts (radius step, property-type word, added-within days, or a page beyond its 1,008-result ceiling). **Not** a claim that there is nothing there |
| `no-filter-match` | Listings were read, but your filters removed all of them. The row says how many were read |
| `no-new-listings` | Monitoring mode: nothing new since the previous run |
| `unreadable` | The page could not be read, its embedded data was missing, or it claimed a total above zero while yielding nothing |

***

### Pricing

Pay per property returned. Rows that report an empty search, an unknown location, a filter that matched nothing, or a page that could not be read are **not** charged. A property that appears twice across pages is dropped before delivery, so it is never charged twice.

***

### Notes

- `robots.txt` was read in full on 2026-09-05. Under `/property-for-sale/` and `/property-to-rent/` it disallows only `contactBranch.html`, `draw-a-search.html`, `map.html`, `market-information/` and `nearby-schools/`. This Actor requests only the listing pages (`find.html` and `<place>.html`) with Rightmove's own query parameters plus `index=N`, and refuses a pasted URL that points at a disallowed path. Student accommodation search is disallowed by Rightmove and is not offered here.
- Only public pages are read. No login, no API key.

# Actor input Schema

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

Paste a Rightmove listing page, for example https://www.rightmove.co.uk/property-for-sale/find.html?locationIdentifier=REGION%5E87490 or https://www.rightmove.co.uk/property-to-rent/Manchester.html, and the two fields below are ignored. Its price, bedroom, type, radius, must-have and sort settings are kept (the sort in the URL wins over the Sort order field below); a keywords= parameter is dropped because Rightmove ignores it - use the Keywords filter here instead.

## `channel` (type: `string`):

sale = property-for-sale, rent = property-to-rent.

## `location` (type: `string`):

A town or area (London, Milton Keynes, Greater Manchester), an outcode (SW1A, M1), or a Rightmove identifier copied from a Rightmove address (REGION^87490, OUTCODE^2506, STATION^5177). Rightmove always resolves a name to some place - Wales is a village in Somerset, Kings Lynn is the station - so every row carries the place Rightmove actually searched in searchedLocation.

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

How many properties to return. You are charged for the rows you actually receive. Rightmove shows at most 1,008 results per search; narrow the search to see more.

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

A hard stop on how many pages are requested, whatever the filters do. 0 lets the run work it out. Rightmove has 42 pages at most.

## `includeSummary` (type: `boolean`):

The teaser Rightmove shows on the card. Turn it off for a smaller dataset - the keyword filter still searches it either way.

## `useProxy` (type: `boolean`):

Off by default because Rightmove answers Apify's own network directly. If Rightmove ever starts blocking, the run switches to a proxy on its own.

## `minPrice` (type: `integer`):

Applied by Rightmove itself. For sale this is a total; to rent it is per calendar month, the way Rightmove takes it. 0 means no minimum.

## `maxPrice` (type: `integer`):

0 means no maximum. A property with no published price (POA) is kept.

## `minBedrooms` (type: `integer`):

-1 turns the filter off. 0 is a studio.

## `maxBedrooms` (type: `integer`):

-1 turns the filter off.

## `propertyTypes` (type: `array`):

Rightmove's own words. Leave empty for all types.

## `radius` (type: `string`):

Rightmove's own steps. 0 means the area itself.

## `sortBy` (type: `string`):

newest = Rightmove's 'Newest Listed', which orders by the last update - a new listing or a price reduction. Rightmove's own default on the site is highest price first.

## `addedOrReducedWithinDays` (type: `string`):

Rightmove's 'Added to site' filter. Note that Rightmove counts a price reduction as an update, so a property listed months ago and reduced yesterday is included. 0 means no limit. Only 1, 3, 7 or 14 are accepted by Rightmove.

## `mustHave` (type: `array`):

Rightmove's own must-have flags.

## `dontShow` (type: `array`):

Rightmove's own exclusion flags.

## `furnishTypes` (type: `array`):

Ignored for sales.

## `includeSSTC` (type: `boolean`):

Also return properties that are sold subject to contract.

## `includeLetAgreed` (type: `boolean`):

Also return properties that are already let agreed.

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

Keep only properties whose title, address, summary, type, features or agency contain these words. Rightmove ignores keywords in its own URL, so this runs here on the rows already fetched. No extra requests are made.

## `keywordMatch` (type: `string`):

any: at least one keyword. all: every keyword.

## `excludeKeywords` (type: `array`):

Drop properties containing any of these words.

## `agencyNames` (type: `array`):

Keep only properties marketed by these agencies. Partial names work.

## `requirePrice` (type: `boolean`):

Off by default: a POA property is not treated as a cheap one, it is simply kept.

## `minBathrooms` (type: `integer`):

-1 turns the filter off. A property whose bathroom count is not published is kept.

## `minSizeSqFt` (type: `integer`):

0 means no minimum. Rightmove publishes a size on some listings only; a property without one is kept.

## `maxSizeSqFt` (type: `integer`):

0 means no maximum.

## `tenureTypes` (type: `array`):

Keep only these tenures. A property whose tenure is not published is kept.

## `excludeCommercial` (type: `boolean`):

Rightmove mixes some commercial listings into residential searches (3 of 150 measured). Drop them.

## `excludeStudents` (type: `boolean`):

Drop listings flagged as student accommodation.

## `listedWithinDays` (type: `integer`):

Unlike Rightmove's own 'added' filter, this uses the date the property first appeared and ignores price reductions. 0 means no date filter. A property whose date cannot be read is kept.

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

Turn this on and schedule the Actor: each run returns only the properties it has never returned before, so you are charged for new stock rather than for the same pages again. Properties that did not fit under the limit are still waiting for you on the next run.

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

Clears the memory for this search, so the next monitoring run starts from scratch. Works with or without monitoring mode on.

## Actor input object example

```json
{
  "channel": "sale",
  "location": "London",
  "maxListings": 20,
  "maxPages": 0,
  "includeSummary": true,
  "useProxy": false,
  "minPrice": 0,
  "maxPrice": 0,
  "minBedrooms": -1,
  "maxBedrooms": -1,
  "propertyTypes": [],
  "radius": "0",
  "sortBy": "newest",
  "addedOrReducedWithinDays": "0",
  "mustHave": [],
  "dontShow": [],
  "furnishTypes": [],
  "includeSSTC": false,
  "includeLetAgreed": false,
  "keywords": [],
  "keywordMatch": "any",
  "excludeKeywords": [],
  "agencyNames": [],
  "requirePrice": false,
  "minBathrooms": -1,
  "minSizeSqFt": 0,
  "maxSizeSqFt": 0,
  "tenureTypes": [],
  "excludeCommercial": false,
  "excludeStudents": false,
  "listedWithinDays": 0,
  "monitoringMode": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per property: the description Rightmove gives it, the address, the price as a number with what it means (a sale total, a monthly or a weekly rent) and Rightmove's own displayed price and qualifier, POA kept as null rather than a number, bedrooms, bathrooms, size in sq ft where published, tenure, coordinates, key features, labels such as NEW\_HOME or LET\_AGREED, the agency and branch, image counts, and the dates it first appeared and was last updated. Every row also names the place Rightmove actually searched. Searches with no results, locations or values Rightmove does not know, filters that matched nothing, and pages that could not be read come back as their own rows and are not charged. Branch phone numbers are not collected.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/rightmove-listings").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/rightmove-listings").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 '{}' |
apify call neverempty/rightmove-listings --silent --output-dataset

```

## MCP server setup

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

```

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/KhNrOYQEfBY5OyW4k/builds/DECaKRDPPWYy9O8dj/openapi.json
