# Rightmove & Zoopla Scraper — UK Property, Sale & Rent (`brilliant_gum/uk-realestate-scraper`) Actor

Scrape both Rightmove and Zoopla in one run. Search by location or URL with price, beds, type, radius and date filters. Full details: price-per-sqft, tenure, EPC, size, council tax, floorplans, nearest stations, agent and price history — plus deal scoring, days-on-market, monitoring and dedup.

- **URL**: https://apify.com/brilliant\_gum/uk-realestate-scraper.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.008 / property record

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 & Zoopla Scraper — UK Property (Sale & Rent)

![Rightmove & Zoopla Scraper](https://api.apify.com/v2/key-value-stores/IuFu0Cw6PpOkNkDV7/records/uk-realestate-scraper-banner.png)

One Apify actor that scrapes **both Rightmove and Zoopla** — the two biggest UK property portals —
for **sale, rent and new homes**, and returns a **single normalized record** per
property. No login, HTTP-first.

Most actors cover only one site. This one does both and returns the same core listing data as the
leading single-site actors — plus fields they typically omit (nearest-station distances, council
tax band, floor area / £-per-sqft, EPC, Zoopla price-change history) and built-in **deal analytics**.

### What you can do

- **Search by location** (town, area or postcode) on either or both sites, with filters: price,
  bedrooms, property type, radius, added-since, sort, SSTC/Let-Agreed, furnishing, let type.
- **Search by URL** — paste any Rightmove or Zoopla search URL; it is paginated automatically.
- **Scrape single properties** — paste direct property URLs from either site.
- **Full property details** — description, key features, all photos, floorplans, EPC, tenure,
  size (sq ft / sq m), council tax band, coordinates, nearest stations (with distance), agent/branch
  - phone, listing date/status and price history (Zoopla).
- **Deal analytics** — price-per-sqft, days-on-market, price-change %, and an area-relative
  **deal score / undervalued %**. Each sale property is compared to the run's median £/sqft using
  the most specific comparable set available — outcode + bedroom count, then outcode, then run-wide
  by bedrooms/site (the basis and comparable count are reported in `analytics`). Auction and
  shared-ownership listings are kept out of the comparison — their guide / share prices aren't
  comparable to full-market £/sqft. No external AI or API keys.
- **Monitoring & dedup** — cross-run dedup key store and "new listings only" monitoring mode.

### Input (highlights)

| Field | Description |
|---|---|
| `site` | `both` | `rightmove` | `zoopla` |
| `listingType` | `sale`, `rent`, `newHomes` (combinable) |
| `searchLocation` / `searchLocations` | town / area / postcode |
| `startUrls` | Rightmove/Zoopla search or property URLs (site & type auto-detected) |
| `minPrice`, `maxPrice`, `minBedrooms`, `maxBedrooms`, `propertyTypes`, `searchRadius`, `addedSince`, `sortBy`, `keywords` | search filters |
| `fetchDetails` | open each property page for the full record (default on) |
| `dealAnalytics` | compute £/sqft, days-on-market, deal score (default on) |
| `maxItems` | per location + listing-type + site |
| `deduplicateKeyStore`, `monitoringMode` | cross-run monitoring |
| `proxy` | Proxy configuration (auto-tuned; optional override) |

### Proxy

- Proxies are configured **automatically** for reliable results on both sites — you don't need to
  set anything up. Just make sure your Apify plan includes proxy access.
- Advanced users can override the `proxy` input, but the defaults are tuned to work out of the box.

### Output

One JSON object per property with a unified schema across both sites: `site`, `id`, `url`,
`listingType`, `displayAddress`, `postcode`/`outcode`, `latitude`/`longitude`,
`price {amount, currency, qualifier, display, frequency}`, `pricePerSqft`, `bedrooms`, `bathrooms`,
`livingRooms`, `propertyType`, `tenure`, `sizeSqft`/`sizeSqm`, `councilTaxBand`, `epc`,
`keyFeatures[]`, `description`, `images[]`, `floorplans[]`, `nearestStations[{name, distance…}]`,
`agent {name, branch, phone, url}`, `firstListed`, `status`, `tags[]`, `priceHistory[]` (Zoopla),
`furnishedState` (Zoopla), `analytics {pricePerSqft, daysOnMarket, priceChangePct, dealScore,
undervaluedPct, valuation, areaMedianPricePerSqft, areaMedianBasis, comparableCount}`, `scrapedAt`,
`dedupKey`. Extra site-specific fields (e.g. lease years, ground rent, EPC rating, chain-free) are
included at the top level too, with a small native subset under `raw`.

> Note: sold-price history (Rightmove/Zoopla "house-prices") is **out of scope** — those pages use a
> different structure; use a dedicated sold-price actor. Rightmove listing pages don't publish
> per-date price history (only the added/reduced date, in `listingUpdate`/`firstListed`); the
> `priceHistory[]` array is populated from Zoopla.

### Pricing

Pay-per-event:

| Event | Price |
|---|---|
| Actor start | $0.01 per run |
| Property record | $0.008 per property (Rightmove or Zoopla) |

Every property record already includes **full details and deal analytics** — there's no separate
charge for scoring. Apify platform usage (compute and proxies) is billed to your own Apify account
on top of these events. Tip: turn off **Fetch full property details** for a faster run that uses
less compute and proxy (search-card fields only).

### Support & feedback

Found a bug, or want another field or portal added? **Open an issue** on the actor's Issues tab, or
leave a **rating and review** on the actor page — it genuinely helps and I read every one.

### Legal

Scrapes only public pages; performs no login or paywall bypass. You are responsible for complying
with each portal's Terms of Use and applicable law (including GDPR where agent contact details or
other personal data are involved).

# Actor input Schema

## `site` (type: `string`):

Which portal(s) to scrape: Rightmove, Zoopla, or both. 'Both' returns a unified, normalized record for each site.

## `listingType` (type: `array`):

What to search: properties for Sale, to Rent, or New homes. You can combine several. Ignored for direct property/search URLs (their type is auto-detected). Note: sold-price history (Rightmove/Zoopla 'house-prices') is out of scope — use a dedicated sold-price actor.

## `searchLocation` (type: `string`):

A single location to search: town, area, or postcode/outcode (e.g. "London", "Manchester", "SW1A"). Resolved automatically on each site. Leave empty if you use Locations or Start URLs.

## `searchLocations` (type: `array`):

Multiple locations to search. Each location is run for each selected listing type on each selected site. Overrides the single Location field when set.

## `startUrls` (type: `array`):

Ready-made Rightmove/Zoopla URLs: search-result pages (paginated automatically) or direct property pages. Site and listing type are auto-detected from the URL. Advanced alternative to Location.

## `searchRadius` (type: `integer`):

Include properties within this many miles of the location (e.g. 0, 1, 3, 5, 10, 15, 20, 30, 40). 0 = this area only.

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

Minimum price. For rentals this is per the chosen price frequency (default per month).

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

Maximum price. For rentals this is per the chosen price frequency (default per month).

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

Minimum number of bedrooms.

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

Maximum number of bedrooms.

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

Keep only these property types (mapped to each site's own categories). Empty = any type.

## `addedSince` (type: `string`):

Only listings added within this window.

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

Result ordering (mapped to each site's sort).

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

For sale: include properties marked Sold STC / Under Offer.

## `includeUnderOffer` (type: `boolean`):

For rent: include properties marked Let Agreed / Under Offer.

## `priceFrequency` (type: `string`):

For rentals: whether min/max price are per month or per week.

## `furnishType` (type: `string`):

For rentals: filter by furnishing state.

## `letType` (type: `string`):

For rentals on Rightmove: long-term or short-term lets.

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

Only listings that have these features (garden, parking, new home, retirement).

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

Exclude these listing categories (Rightmove): new homes, retirement, shared ownership.

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

Only listings whose description/features contain these keywords (e.g. "garden", "garage", "period").

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

Maximum properties per location + listing-type + site. 0 = all available (safety-capped by Max pages).

## `maxPagesPerSearch` (type: `integer`):

Maximum result pages to fetch per search (about 24-25 properties per page). 0 = until Max properties is reached or results run out.

## `fetchDetails` (type: `boolean`):

Open each property page for the full record (description, floorplans, EPC, nearest stations, council tax, size, tenure, price history on Zoopla). Turn off for a faster/cheaper run using only search-card fields.

## `includePriceHistory` (type: `boolean`):

Include price-change history and price-change analytics when available (requires full details). Available on Zoopla; Rightmove listing pages don't publish per-date price history (only the added/reduced date, returned in listingUpdate/firstListed).

## `dealAnalytics` (type: `boolean`):

Compute price-per-sqft, days-on-market, and an area-relative deal score / undervalued % (comparing each property to the run's median £/sqft for its outcode + bedroom count). No external AI or keys.

## `deduplicate` (type: `boolean`):

Drop properties already produced (within the run, and across runs when a dedup key store is set).

## `deduplicateKeyStore` (type: `string`):

Named key-value store to remember seen property IDs across scheduled runs. Empty = in-run dedup only.

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

Output only properties not seen in a previous run (requires a dedup key store). Ideal for new-listing alerts.

## `maxConcurrency` (type: `integer`):

Parallel property-detail requests. Zoopla is heavier — 5-10 is a good range.

## `maxRequestRetries` (type: `integer`):

Retries with a fresh proxy session on temporary blocks, timeouts or server errors.

## `proxy` (type: `object`):

Proxy configuration. Leave as-is — proxies are tuned automatically for reliable results on both sites. Advanced users can override this.

## Actor input object example

```json
{
  "site": "both",
  "listingType": [
    "sale"
  ],
  "searchLocation": "London",
  "searchRadius": 0,
  "addedSince": "",
  "sortBy": "",
  "includeSSTC": false,
  "includeUnderOffer": false,
  "priceFrequency": "per_month",
  "furnishType": "",
  "letType": "",
  "maxItems": 20,
  "maxPagesPerSearch": 0,
  "fetchDetails": true,
  "includePriceHistory": true,
  "dealAnalytics": true,
  "deduplicate": true,
  "monitoringMode": false,
  "maxConcurrency": 8,
  "maxRequestRetries": 4,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (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 = {
    "listingType": [
        "sale"
    ],
    "searchLocation": "London",
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/uk-realestate-scraper").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 = {
    "listingType": ["sale"],
    "searchLocation": "London",
    "maxItems": 20,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("brilliant_gum/uk-realestate-scraper").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 '{
  "listingType": [
    "sale"
  ],
  "searchLocation": "London",
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call brilliant_gum/uk-realestate-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,brilliant_gum/uk-realestate-scraper"
        }
    }
}
```

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/1VjVkeKkkNjG3QA0M/builds/BuHb9rSxfmDtaHRaW/openapi.json
