# Rightmove Property Details Scraper - Price, Features & History (`lukehunter/rightmove-property-details`) Actor

Get full Rightmove listing details from property URLs or IDs: price, status, address, coordinates, bedrooms, tenure, council tax band, features, description, agent branch, floorplans, stations. Change mode sends price and status change alerts to Discord, Telegram, Slack or any webhook. No login.

- **URL**: https://apify.com/lukehunter/rightmove-property-details.md
- **Developed by:** [Luke Hunter](https://apify.com/lukehunter) (community)
- **Categories:** Real estate, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 properties

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Rightmove Property Details Scraper - Price, Features & History

**For estate agents, property investors, proptech teams and lenders who already have a list of Rightmove listings and need the full detail of each one.** Give it property URLs or IDs and it returns one clean row per property - price, status (available, let agreed, removed), address, coordinates, bedrooms, bathrooms, tenure, council tax band, key features, description, listing history, agent branch, floorplans, photos and nearest stations - read from Rightmove's own public property page. No login, no proxies, no private individuals' contact details. Run it on a schedule with the **change feed** (`onlyChangedSinceLastRun`) and you only get - and pay for - properties whose price or status changed.

Pay-per-result: **$0.003 per property — 1,000 properties = $3.** No start fee.

### Quick start (1 minute)

1. Open the **Input** tab and paste one or more Rightmove property URLs (`https://www.rightmove.co.uk/properties/<id>`), or ids into **Rightmove property IDs**.
2. Click **Start**. The result is one row per property in the dataset - export it as JSON, CSV or Excel, or pull it through the API shown below.

```json
{
  "urls": ["https://www.rightmove.co.uk/properties/93630033"],
  "maxItems": 10
}
```

That prefilled example finishes in a few seconds and costs $0.003.

### Who it is for

- **Estate and letting agents**: track competing listings and the asking-price history of properties in your patch.
- **Property investors and portfolio owners**: monitor a watch-list of listings for price cuts, "let agreed"/"sold STC" and withdrawals.
- **Proptech and data teams**: enrich your own listings, valuation or yield models with price, size, tenure, council tax band, features and nearest stations - fetched by id from code.
- **Lenders and valuers**: pull the advertised price, tenure, council tax band and photos for a specific address's listing.

### How it works

Every Rightmove property page carries its whole listing as structured data in the page itself. This Actor:

1. Normalises each input to the canonical `https://www.rightmove.co.uk/properties/<id>` URL (anything that is not a property page is reported as an error for that entry, and the rest carry on).
2. Reads `robots.txt` once per run and checks each property's own path before requesting it. If `robots.txt` cannot be read, nothing is fetched.
3. Requests one property page at a time, about one every 3 seconds, with an honest, self-identifying User-Agent. No proxies, no cookies, no login, no CAPTCHA handling. If Rightmove blocks the run (HTTP 403/429 three times in a row) it stops and says so rather than pressing on.
4. Decodes the page's embedded data into one flat row. A listing that has been taken down (Rightmove answers HTTP 410) becomes a row with `status: "removed"`.
5. Delivers and charges each row as it goes, never past your run's spending limit.

Plan on about 3 seconds per property: 100 properties take roughly 5 minutes, 1,000 about 50 minutes.

### Use cases

- **Price-drop alerts on a watch-list**: schedule the change feed daily and get a row only when a property's price or status changes.
- **Withdrawn and let-agreed tracking**: a listing that disappears is delivered once with `status: "removed"`; "let agreed" and "sold STC" are reported as published.
- **Enrich search results**: take the `propertyId` list from the [Rightmove Scraper](https://apify.com/lukehunter/rightmove-scraper) search Actor and fetch the full details for each.
- **Comparable-evidence pack**: price, size, tenure, council tax band, key features, floorplans and photos for a shortlist of comparable listings.

### Change feed: only price and status changes

Turn on `onlyChangedSinceLastRun` and put the Actor on a **schedule**:

```json
{
  "propertyIds": [93630033, 93478416],
  "onlyChangedSinceLastRun": true,
  "stateStoreName": "my-rightmove-watchlist"
}
```

1. **The first run is a baseline**: every property is new to the watch, so every property is delivered (and charged) and its price and status remembered.
2. **Every later run delivers only what changed**: a different price, price qualifier or status, or a property it has not seen before. Unchanged properties are checked but not delivered and not charged.
3. **A listing that disappears is delivered once** with `status: "removed"`, then stays quiet. If it is relisted, it is delivered again.
4. Edits to the description, photos or features alone are not changes; only price and status are.
5. Add a **Webhook** for `ACTOR.RUN.SUCCEEDED` on the schedule to push each run's rows into Slack, e-mail, Make, Zapier or your own endpoint. A run with no changes still succeeds, with an empty dataset and a status message saying so.

The state is kept in the named key-value store `stateStoreName`, so use a different name per watch-list.

### Get Rightmove alerts in Discord, Telegram, Slack, Teams, Google Chat, your phone (ntfy/Pushover) or any app (webhook)

Want a **Rightmove price drop alert**, a **Rightmove Telegram bot**, or listing changes pushed to Discord, Slack, Teams, Google Chat or your phone instead of checking a dataset? Turn on the change feed (`onlyChangedSinceLastRun`), add one or more destinations in the Actor's **Notifications (optional)** section and each run sends the properties whose price or status changed, or that were taken down. It is a **monitor that notifies you**: it only reads public Rightmove property pages and never contacts agents, buys anything or logs in anywhere.

| Channel | Fill in | What you get |
|---|---|---|
| Discord | `discordWebhookUrl` | One card per property change, 10 per message |
| Telegram | `telegramBotToken` + `telegramChatId` | Up to 10 property changes per message, one line each |
| Slack | `slackWebhookUrl` | Block Kit message, up to 10 property changes per message |
| Mattermost, Rocket.Chat | `slackCompatibleWebhookUrl` | Slack-style incoming webhook: a Markdown list (these apps do not render Slack Block Kit) |
| Microsoft Teams | `teamsWebhookUrl` | Adaptive Card from a Teams Workflows webhook, up to 10 property changes per card |
| Google Chat | `googleChatWebhookUrl` | Card in a space, up to 10 property changes per message |
| ntfy (phone push, no account) | `ntfyTopic` (optional `ntfyServer`, `ntfyToken`) | A push per property change, up to 10, then one "...and N more" |
| Pushover (phone push) | `pushoverUserKey` + `pushoverAppToken` | A push per property change, up to 10, then one "...and N more" |
| Any app: Zapier, Make, n8n, Pipedream, IFTTT, Home Assistant | `webhookUrl` | One JSON POST per run with the changed rows |

Fill in as many as you like; each channel works on its own and a failing one never stops the others.

#### Setup, channel by channel

- **Discord (1 minute):** Server Settings → Integrations → Webhooks → New Webhook → pick the channel → **Copy Webhook URL**.
- **Telegram (2 minutes):** chat with **@BotFather**, send `/newbot`, paste the token into `telegramBotToken`. Add the bot to the chat or channel (administrator for a channel; for a chat with yourself press **Start** on the bot). `telegramChatId` is a numeric id (`123456789`, or `-1001234567890` for a group or channel) or a public `@channelusername`.
- **Slack:** at api.slack.com/apps create an app (**From scratch**) → **Incoming Webhooks** → switch it on → **Add New Webhook to Workspace** → pick the channel → copy the `https://hooks.slack.com/services/...` URL.
- **Mattermost or Rocket.Chat:** create an **incoming webhook** in the app and paste its public `https://` URL into `slackCompatibleWebhookUrl`. Localhost, private IP addresses and `.local` / `.internal` names are rejected.
- **Microsoft Teams:** in the channel open **... → Workflows** → **Post to a channel when a webhook request is received** → finish the wizard and copy the URL (a host ending in `.logic.azure.com` or `.powerplatform.com`). The old "Office 365 connector" webhooks are retired and not supported.
- **Google Chat:** in the space open **Apps & integrations → Webhooks → Add webhook**, name it and copy the `https://chat.googleapis.com/v1/spaces/...` URL (needs a Google Workspace account that allows webhooks).
- **ntfy (free, no account):** install the ntfy app (or open ntfy.sh), subscribe to a topic with a long random name, and put the same name in `ntfyTopic`. Anyone who knows the topic can read it, so make it unguessable. Own server or a protected topic: set `ntfyServer` (public `https://` address) and `ntfyToken`.
- **Pushover:** copy your user key from pushover.net into `pushoverUserKey`, create an application at pushover.net/apps/build and put its API token in `pushoverAppToken`. Pushover itself is a separate paid app after its trial: check their pricing.
- **Any app (webhook):** paste the catch-hook URL from Zapier, Make, n8n, Pipedream, IFTTT or Home Assistant into `webhookUrl`. It must be a public `https://` address.

#### The recipe: a Rightmove property watch in five steps

1. List the properties to watch in `urls` and/or `propertyIds`.
2. Turn on `onlyChangedSinceLastRun`. Alerts only ever cover changes, so they need this mode.
3. Add your destination(s) from the table above.
4. Run it once. The first run is the baseline: it delivers and remembers every property and sends nothing.
5. Create a **Schedule** (for example daily). From then on you hear about a price or status change (let agreed, sold STC) or a listing that was removed.

```json
{
  "urls": [
    "https://www.rightmove.co.uk/properties/93630033"
  ],
  "onlyChangedSinceLastRun": true,
  "maxItems": 100,
  "ntfyTopic": "your-long-random-topic-name",
  "maxNotifications": 25
}
```

#### The generic webhook (`webhookUrl`)

Each run sends **one POST per 100 changed rows** (so usually one) with this body, followed by whatever your tool does with it:

```json
{
  "source": "apify",
  "actor": "rightmove-property-details",
  "runId": "HG7ML7M8z78YcAPEB",
  "count": 2,
  "total": 2,
  "part": 1,
  "parts": 1,
  "items": [ { "...": "the full dataset rows" } ]
}
```

`count` is the number of rows in this POST, `total` the number in the run, and `part` / `parts` number the POSTs when a run delivers more than 100 rows. `maxNotifications` does not cap the webhook. Your endpoint should answer with a 2xx status; redirects are not followed and a failure is reported in the run status, never retried in a loop.

#### What to expect

- **What a message contains:** what changed and what the listing looks like now: the status, the price (with Rightmove's own wording and qualifier), Rightmove's "Reduced on ..." history line when the page carries it, bedrooms, bathrooms, type, size, the agent branch, a photo and the link. A removed listing says "Removed".
- **Only changes are sent.** The first run (the baseline) and a normal run without change mode deliver every property rather than changes, so they send nothing and the status message says why. A run where nothing changed sends nothing.
- **An alert shows the listing as it is now, not "was X".** The change feed remembers a fingerprint of price and status, not the old price, so an alert names the new price and status rather than the previous one.
- **`maxNotifications` (default 25, max 100)** caps how many property changes go to chat and phone channels per run (the webhook is not capped). The rest are still in the dataset and the status message says so. ntfy and Pushover send at most 10 individual pushes, then one "...and N more" summary.
- **Cost: notifications are free.** You pay only for delivered properties at the usual price, exactly as without alerts. There is no extra charge for any channel.
- **Failures never fail the run.** A deleted webhook or an unreachable service leaves the run successful and the dataset complete, and the status message says what happened, for example `Notified: Discord 12, Slack 12, ntfy 10 (+summary). Teams failed (HTTP 404) - check the webhook URL.` The logs show the channel name and HTTP status only, never a URL or token.
- **Your URLs, tokens and ntfy topic are stored as secret inputs.** Anyone holding a webhook URL can post to that channel, so treat it like a password.
- **The dataset is unchanged.** The output rows are identical whether or not you set a destination; the run's status message just gains a short note such as `Notified: Discord 12, ntfy 10 (+summary).`
- **It is a monitor that notifies you.** It never contacts agents, buys anything or logs in to Rightmove, and the messages go only to the destinations you set up.

### Input

```json
{
  "urls": ["https://www.rightmove.co.uk/properties/93630033"],
  "propertyIds": [],
  "maxItems": 100,
  "onlyChangedSinceLastRun": false,
  "stateStoreName": "rightmove-property-details-seen"
}
```

| Field | Type | Default | Description |
|---|---|---:|---|
| `urls` | string\[] | *(none)* | Rightmove property URLs, `https://www.rightmove.co.uk/properties/<id>`. Search-result URLs are rejected. Up to 5,000. |
| `propertyIds` | (string or number)\[] | *(none)* | Property ids, e.g. `93630033`. Use instead of or alongside `urls`; a repeated id is checked once. Up to 5,000. At least one of `urls` / `propertyIds` is required. |
| `maxItems` | integer | 100 | 1-5,000. Hard cap on how many properties are checked, and so on rows delivered and cost. Entries beyond it are reported as not checked. |
| `onlyChangedSinceLastRun` | boolean | `false` | Change feed - see above. |
| `stateStoreName` | string | `rightmove-property-details-seen` | Name of the store that remembers each property's last price and status. Letters, numbers and hyphens. |
| `discordWebhookUrl` | string (secret) | *(none)* | Discord webhook URL. Must look like `https://discord.com/api/webhooks/<id>/<token>`. Empty means no Discord messages. |
| `telegramBotToken` | string (secret) | *(none)* | Telegram bot token from @BotFather. Needs `telegramChatId`. |
| `telegramChatId` | string | *(none)* | Telegram chat to post to: a numeric chat id or `@channelusername`. The bot must be added to that chat or channel. Needs `telegramBotToken`. |
| `slackWebhookUrl` | string (secret) | *(none)* | Slack incoming webhook, `https://hooks.slack.com/services/...`. |
| `slackCompatibleWebhookUrl` | string (secret) | *(none)* | Mattermost / Rocket.Chat incoming webhook (public `https://` URL; Markdown list, no Block Kit). |
| `teamsWebhookUrl` | string (secret) | *(none)* | Microsoft Teams Workflows webhook (host ending `.logic.azure.com` or `.powerplatform.com`). |
| `googleChatWebhookUrl` | string (secret) | *(none)* | Google Chat space webhook, `https://chat.googleapis.com/v1/spaces/...`. |
| `ntfyTopic` | string (secret) | *(none)* | ntfy topic name; setting it turns ntfy phone pushes on. Pick a long, unguessable name. |
| `ntfyServer` | string | `https://ntfy.sh` | ntfy server. Only change it for your own public `https://` ntfy server. |
| `ntfyToken` | string (secret) | *(none)* | Optional ntfy access token for a self-hosted server or protected topic. Needs `ntfyTopic`. |
| `pushoverUserKey` | string (secret) | *(none)* | Pushover user key. Needs `pushoverAppToken`. |
| `pushoverAppToken` | string (secret) | *(none)* | Pushover application API token. Needs `pushoverUserKey`. |
| `webhookUrl` | string (secret) | *(none)* | Generic JSON webhook (Zapier, Make, n8n, Pipedream, IFTTT, Home Assistant): the changed rows, 100 per POST. Public `https://` only. |
| `maxNotifications` | integer | 25 | 1–100. Most property changes sent to chat and phone channels per run (the webhook is not capped). Extra rows are still in the dataset. Only used when a destination is set. |

Errors for individual entries (invalid URL, blocked, page not recognised, ...) are listed in the run's status message and, one per input, in the `RUN_REPORT` record of the run's key-value store. Properties with an error are never charged.

### Output fields

| Category | Fields |
|---|---|
| Identity | `propertyId`, `url`, `inputUrl` |
| State | `status`, `listedDate`, `listingHistory`, `checkedAt` |
| Price | `channel`, `price`, `priceText`, `priceQualifier`, `currency`, `pricePerSqft` |
| Location | `displayAddress`, `postcodeOutcode`, `latitude`, `longitude`, `nearestStations` |
| Property | `propertyType`, `bedrooms`, `bathrooms`, `sizeSqft`, `tenure`, `councilTaxBand`, `keyFeatures`, `description` |
| Lettings | `availableFrom`, `deposit`, `furnishType` |
| Agent (business) | `agentBranchName`, `agentCompany` |
| Media | `floorplanUrls`, `imageUrls` (first 10) |

Missing values are returned as `null` (lists as `[]`). Nothing is invented. `status` is `"available"`, or `"let agreed"` / `"sold STC"` / `"under offer"` when the page says so, or `"removed"`.

### Example output

A live letting (description shortened here; the dataset has the full text):

```json
{
  "propertyId": 93630033,
  "url": "https://www.rightmove.co.uk/properties/93630033",
  "status": "available",
  "channel": "rent",
  "price": 950,
  "priceText": "£950 pcm",
  "priceQualifier": null,
  "currency": "GBP",
  "pricePerSqft": null,
  "displayAddress": "Brookfield Road, Manchester, M8",
  "postcodeOutcode": "M8",
  "latitude": 53.514988,
  "longitude": -2.235929,
  "propertyType": "Flat",
  "bedrooms": 2,
  "bathrooms": 1,
  "sizeSqft": null,
  "tenure": null,
  "councilTaxBand": null,
  "keyFeatures": ["No Agent Fees", "Students Can Enquire", "Property Reference Number: 2431988"],
  "description": "Property Reference: 2431988.\n\nThis two bed ground floor flat is located in a great location. A short walk to Crumpsall tram stop and on a main bus route ...",
  "listedDate": "2026-09-27",
  "listingHistory": "Added on 27/09/2026",
  "availableFrom": "09/10/2026",
  "deposit": 950,
  "furnishType": "Furnished or unfurnished, landlord is flexible",
  "agentBranchName": "OpenRent, London",
  "agentCompany": "OpenRent",
  "floorplanUrls": [],
  "imageUrls": ["https://media.rightmove.co.uk/property-photo/6b3ed9cc1/93630033/6b3ed9cc1d8c206318b471b8646c1c2e.jpeg"],
  "nearestStations": [
    { "name": "Crumpsall Tram Stop", "distanceMiles": 0.27 },
    { "name": "Woodlands Road Tram Stop", "distanceMiles": 0.45 },
    { "name": "Bowker Vale Tram Stop", "distanceMiles": 0.85 }
  ],
  "checkedAt": "2026-10-03T12:00:00.000Z",
  "inputUrl": "https://www.rightmove.co.uk/properties/93630033"
}
```

### Pricing and cost control

Pay per delivered property.

**$0.003 per property — 1,000 properties = $3.**

| Properties delivered | Cost |
|---:|---:|
| 10 | $0.03 |
| 1,000 | $3.00 |
| 5,000 | $15.00 |

There is no charge for starting a run, and no charge for an input that errors (invalid, blocked, not recognised, skipped by `robots.txt`) or, in change-feed mode, for a property that has not changed. A confirmed-removed listing **is** a delivered row and is charged like any other (in change-feed mode only once). `maxItems` and your run's maximum cost per run are hard upper bounds: the Actor never delivers a row your spending limit cannot pay for.

Notifications to Discord, Telegram, Slack, Teams, Google Chat, ntfy, Pushover and webhooks are free: you pay only for delivered properties, never for messages sent.

### Limitations

- **Property pages only.** The input is a property URL or id. For searches by area, price or bedrooms use the [Rightmove Scraper](https://apify.com/lukehunter/rightmove-scraper) and feed its `propertyId`s here.
- **Business data only.** Agent branch name and company only. No agent or landlord telephone numbers, e-mail addresses or street addresses, and no private individuals' details. The advert `description` is the text the agent published.
- **Not a sold-price or history database.** `listedDate` and `listingHistory` are what the page publishes ("Added on ..."). Price history over time comes from running the change feed on a schedule; the Actor cannot see prices from before your first run.
- **Removed listings carry their last published details.** A `removed` row shows what the page still carried when Rightmove took the listing down (price, address, photos), not a current price. Use `status` first.
- **Sparse fields stay `null`.** Size, tenure, council tax band and price per sq ft are only filled when the listing publishes them; many listings say "ask agent".
- **Politeness limits speed.** About 3 seconds per property, one at a time. If Rightmove blocks or rate-limits the run, the Actor stops and reports it; it never tries to get around a block.
- This is an independent tool, not affiliated with, endorsed by, or connected to Rightmove. You are responsible for using the data in line with Rightmove's terms and applicable law.

### Use it from your code, Make, Zapier or n8n

Built for automated pipelines: small runs, stable field names, one row per item, and you pay only for rows actually delivered.

**One API call that runs the Actor and returns the data** (replace `$APIFY_TOKEN` with your token from Apify Console → Settings → Integrations):

```bash
curl -s -X POST "https://api.apify.com/v2/acts/lukehunter~rightmove-property-details/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"urls": ["https://www.rightmove.co.uk/properties/93630033"], "maxItems": 10}'
```

**JavaScript** (`npm i apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('lukehunter~rightmove-property-details').call({
  "urls": [
    "https://www.rightmove.co.uk/properties/93630033"
  ],
  "maxItems": 10
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python** (`pip install apify-client`):

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('lukehunter~rightmove-property-details').call(run_input={"urls": ["https://www.rightmove.co.uk/properties/93630033"], "maxItems": 10})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

**Schedules, webhooks and no-code tools:** save your input as a Task, add a Schedule (e.g. hourly or daily), and attach a webhook or one of the Integrations tab's connectors (Make, Zapier, n8n, Google Sheets, Slack) to push each run's results into your own system.

**Only new items each run:** set `"onlyChangedSinceLastRun": true` on a scheduled Task and every run after the first delivers, and charges for, only items it hasn't delivered before for that same input. That makes it a ready-made change feed.

**Reliability:** an automated test runs this Actor's parser against the live site every day, and failures are fixed fast.

**Feedback:** an honest rating on the Store page, good or bad, helps others decide if this Actor fits their needs. Missing a field or found a problem? Open an issue on the Actor page.

### FAQ

#### Where do I get property ids?

They are the number at the end of a Rightmove property URL (`.../properties/93630033`), and the `propertyId` field of the [Rightmove Scraper](https://apify.com/lukehunter/rightmove-scraper) search results, which this Actor's `propertyId` matches.

#### What does a `removed` row mean, and am I charged for it?

Rightmove answers a taken-down listing with "gone" (HTTP 410). The Actor reports that as a row with `status: "removed"`, which is a delivered row and is charged like any other. In change-feed mode it is delivered once, then not again.

#### Can I check sale and rental listings in the same run?

Yes. `channel` is `"sale"` or `"rent"` per row. For rentals `price` is the rent shown in `priceText` (usually per calendar month).

#### Why is `price` null?

The listing shows "POA" or another non-numeric price. `priceText` always holds the price exactly as displayed.

#### Why did my run say "not checked"?

You supplied more entries than `maxItems`, your spending limit was reached, or the run stopped after repeated blocks. Those entries were not requested and not charged; the status message and `RUN_REPORT` say which.

#### Is the data private-individual data?

No. Only listing and business fields are read; see Limitations.

#### Can I get Rightmove alerts by email?

Not natively. There are two routes. (1) Set `webhookUrl` to a Zapier, Make or n8n catch hook and add an email or Gmail step: the webhook delivers the property changes as JSON, so the email can list them. (2) Apify's own run integrations and run notifications can email you when a run finishes, but they only send a link to the run or dataset, not the property changes themselves. For instant alerts without building anything, Telegram, ntfy or Pushover reach your phone directly.

#### Can I get Rightmove alerts in WhatsApp?

Not natively, and we do not use unofficial WhatsApp APIs, which risk getting your number banned. The official route is the WhatsApp Business Platform (Cloud API) with its own business account, message templates and pricing: point `webhookUrl` at a Zapier, Make or n8n flow that calls it. For free phone alerts use Telegram or ntfy instead.

### Related Actors

Other data tools from the same developer, built to the same standard: official or public sources, hard cost caps, and honest documentation of limits.

- **[Rightmove Property Scraper](https://apify.com/lukehunter/rightmove-scraper)**: UK property listings from Rightmove search results, with price, address, bedrooms and agent details.
- **[County Multifamily Building Permits Surge Radar](https://apify.com/lukehunter/county-multifamily-permit-surge-radar)**: U.S. counties where 5+ unit apartment permits are surging year over year.
- **[Zillow ZIP Code Property Search Scraper](https://apify.com/lukehunter/zillow-zip-scraper)**: for-sale listings by ZIP code with prices, price cuts and days on Zillow.
- **[Zillow Property Details Scraper](https://apify.com/lukehunter/zillow-property-details)**: full property records by URL or zpid: Zestimate, price and tax history, schools, with a change-only mode.
- **[Zillow Price Drop & New Listing Monitor](https://apify.com/lukehunter/zillow-price-drop-monitor)**: stateful ZIP-code change events for Zillow: new listings, price cuts and removed listings, run on a schedule.
- **[Federal Register Rule Effective-Date Radar](https://apify.com/lukehunter/federal-rule-effective-date-radar)**: final federal rules about to take effect, with citations, dockets and urgency.
- **[Chicago Contract Recompete & Extension Radar](https://apify.com/lukehunter/chicago-contract-expiration-radar)**: expiring City of Chicago contracts with extension history, for recompete prospecting.
- **[Bank Branch Closings & Merger Radar (FDIC)](https://apify.com/lukehunter/bank-branch-merger-radar)**: new US bank branch closings, openings and mergers from the FDIC.

# Actor input Schema

## `urls` (type: `array`):

Rightmove property page URLs, e.g. https://www.rightmove.co.uk/properties/93630033 (copy them from your browser, or build them from the propertyId of a Rightmove Scraper search result). Search-result URLs are not accepted here. Up to 5,000 per run; use "propertyIds" instead if you only have ids.

## `propertyIds` (type: `array`):

Rightmove property ids, e.g. 93630033 - the number at the end of a /properties/ URL. Use this instead of, or alongside, "urls". An id given twice (or as both a URL and an id) is checked once. Up to 5,000 per run.

## `maxItems` (type: `integer`):

Hard cap on how many properties are checked, and so on how many rows are delivered and charged, 1-5000, default 100. You are charged $0.003 per delivered property, so this is your cost cap. Entries beyond the cap are reported as not checked.

## `onlyChangedSinceLastRun` (type: `boolean`):

Turn this on and run the Actor on a schedule to get only properties whose price or status (available, under offer, let agreed, removed ...) changed since the previous run, plus any property it has not seen before. Unchanged properties are skipped and never charged. A listing that disappears is delivered once with status "removed". Off by default so a one-off run always returns every property.

## `stateStoreName` (type: `string`):

Only used when "Change feed" is on. Name of the key-value store that remembers each property's last price and status, so it persists across scheduled runs. Leave the default unless you run several tasks whose change history must stay separate. Letters, numbers and hyphens only.

## `discordWebhookUrl` (type: `string`):

Post results to a Discord channel. In Discord: Server Settings -> Integrations -> Webhooks -> New Webhook -> choose the channel -> Copy Webhook URL. Looks like https://discord.com/api/webhooks/<id>/<token>. Leave empty to skip Discord.

## `telegramBotToken` (type: `string`):

Post results to Telegram. Create a bot by chatting with @BotFather on Telegram (send /newbot) and paste the token it gives you, e.g. 123456789:AA.... Needs "Telegram chat ID" below. Leave empty to skip Telegram.

## `telegramChatId` (type: `string`):

Where the bot should post: a numeric chat id (e.g. 123456789, or -1001234567890 for a group or channel) or a public @channelusername. The bot must be added to that chat or channel first (as an administrator for a channel), and for a private chat you must press Start on the bot. Needs "Telegram bot token" above.

## `slackWebhookUrl` (type: `string`):

Post results to a Slack channel (Block Kit cards, up to 10 per message). Create an incoming webhook at api.slack.com/apps -> Create New App -> Incoming Webhooks -> Add New Webhook to Workspace. Looks like https://hooks.slack.com/services/T000/B000/XXXX. Leave empty to skip Slack.

## `slackCompatibleWebhookUrl` (type: `string`):

Post results to a Mattermost or Rocket.Chat incoming webhook, which accept Slack-style webhook payloads. Must be a public https:// address (no localhost, private IP or .local/.internal hosts). These apps do not render Slack Block Kit, so they get a plain Markdown list. For real Slack use "Slack webhook URL" above. Leave empty to skip.

## `teamsWebhookUrl` (type: `string`):

Post results to a Teams channel as an Adaptive Card. In Teams: channel -> ... -> Workflows -> "Post to a channel when a webhook request is received" -> copy the URL. The host must end in .logic.azure.com or .powerplatform.com. The retired Office 365 connector webhooks are not supported. Leave empty to skip Teams.

## `googleChatWebhookUrl` (type: `string`):

Post results to a Google Chat space. In the space: Apps & integrations -> Webhooks -> Add webhook -> copy the URL. Looks like https://chat.googleapis.com/v1/spaces/<space>/messages?key=...\&token=.... Leave empty to skip Google Chat.

## `ntfyTopic` (type: `string`):

Get a phone push with no account via ntfy (ntfy.sh). Install the ntfy app, subscribe to a topic name, and enter the same name here. Anyone who knows the topic can read it, so pick a long, unguessable name (letters, numbers, - and \_). At most 10 individual pushes per run, then one "...and N more". Leave empty to skip ntfy.

## `ntfyServer` (type: `string`):

Only used with "ntfy topic". Leave as https://ntfy.sh, or enter the public https:// address of your own ntfy server.

## `ntfyToken` (type: `string`):

Only used with "ntfy topic", for a self-hosted server or a reserved topic that needs a login. An access token from ntfy (starts with tk\_). Leave empty for open topics.

## `pushoverUserKey` (type: `string`):

Get a phone push via Pushover. Your user key is shown on the Pushover dashboard (pushover.net). Needs "Pushover app token" below. At most 10 individual pushes per run, then one "...and N more". Leave empty to skip Pushover.

## `pushoverAppToken` (type: `string`):

The API token of a Pushover application you create at pushover.net/apps/build (name it after this Actor). Needs "Pushover user key" above.

## `webhookUrl` (type: `string`):

POST the delivered rows as JSON to any public https:// URL: {source, actor, runId, count, items}. At most 100 rows per POST; every delivered row is sent regardless of the max-notifications cap. Use it to route results to email, a spreadsheet or any app. Localhost, private IP and .local/.internal addresses are rejected. Leave empty to skip.

## `maxNotifications` (type: `integer`):

Caps how many results are sent to chat and phone channels per run (Discord, Telegram, Slack, Teams, Google Chat, ntfy, Pushover), 1-100, default 25. Extra results are still in the dataset. The generic webhook is not capped. Only used when a destination above is set.

## Actor input object example

```json
{
  "urls": [
    "https://www.rightmove.co.uk/properties/93630033"
  ],
  "maxItems": 10,
  "onlyChangedSinceLastRun": false,
  "stateStoreName": "rightmove-property-details-seen",
  "ntfyServer": "https://ntfy.sh",
  "maxNotifications": 25
}
```

# Actor output Schema

## `properties` (type: `string`):

No description

# 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 = {
    "urls": [
        "https://www.rightmove.co.uk/properties/93630033"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("lukehunter/rightmove-property-details").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 = {
    "urls": ["https://www.rightmove.co.uk/properties/93630033"],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("lukehunter/rightmove-property-details").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 '{
  "urls": [
    "https://www.rightmove.co.uk/properties/93630033"
  ],
  "maxItems": 10
}' |
apify call lukehunter/rightmove-property-details --silent --output-dataset

```

## MCP server setup

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

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/9af6ppmpQyspn6REK/builds/eysAdN6dGesTyyZ63/openapi.json
