# Zoopla Scraper: UK Property for Sale & Rent (`reventadata/zoopla-scraper`) Actor

Scrape Zoopla property listings for sale or to rent in the UK: price, bedrooms, address with coordinates, property type, agent name, phone and profile link, published date, summary, photo and floor plan counts. Filter by price and bedrooms. Unofficial.

- **URL**: https://apify.com/reventadata/zoopla-scraper.md
- **Developed by:** [reventadata](https://apify.com/reventadata) (community)
- **Categories:** Real estate, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

> **Unofficial.** This Actor is not affiliated with, authorised by, endorsed by, or in any
> way officially connected to Zoopla. "Zoopla" is a trademark of its respective owner
> and is used here only to describe what this tool reads. It reads the same public pages a
> browser does.

Zoopla Scraper reads UK property listings from [Zoopla](https://www.zoopla.co.uk), for sale or
to rent: price, bedrooms, address and map position, property type, the agent or developer that
listed it with their public phone number, photo and floor plan counts, when the listing was
published and, when the listing gives them, bathrooms, floor area (`sizeSqft`, square feet) and price reductions.

### What data can you extract from Zoopla?

The listings in Zoopla's search results for the locations you give (up to 1,000 per location and your row and page caps), with the fields the site
itself publishes there. The Actor reads Zoopla's public search pages, so it
needs no browser and no login.

### Why scrape Zoopla?

- **For sale and to rent**, any location Zoopla has a search page for (`london`, `manchester`,
  `birmingham` and so on), several locations in one run.
- **The site's own filters.** Minimum bedrooms, minimum and maximum price and the sort order are
  sent to Zoopla, so only matching listings are fetched.
- **Rent is the monthly figure.** Zoopla's own numeric rent field is sometimes the weekly
  amount; the Actor reads the monthly rent from the price Zoopla shows (`£1,900 pcm`) and also
  returns the weekly rent separately.
- **No duplicates.** A listing found twice, on another page or under another location, is
  returned once.
- **A clear ending.** A search that matches nothing ends with a status message. A location the
  site does not know, or a run where Zoopla's pages could not be read, ends as failed with the reason.

### How to scrape Zoopla

1. Give one or more location slugs in `locations` and pick `listingType`.
2. Optionally set minimum bedrooms, a price range and the sort order.
3. Run it. Results land in the dataset.

#### Input

| Field | Type | Meaning |
|---|---|---|
| `locations` | array | Zoopla location slugs, for example `london`, `manchester`. Copy the slug from a zoopla.co.uk search URL (`/for-sale/property/<slug>/`). A slug the site does not resolve ends the run with "location not found". |
| `listingType` | string | `for-sale` or `to-rent`. |
| `minBedrooms` | integer | Optional. |
| `minPrice`, `maxPrice` | integer | Optional, pounds. The asking price when for sale, the monthly rent when to rent. |
| `sortBy` | string | `recommended` (Zoopla's default) or `lowestPrice`. |
| `maxItems` | integer | Cap on rows across all locations. |
| `maxPagesPerLocation` | integer | Optional cap on result pages per location, 25 listings each, at most 40. |
| `proxyConfiguration` | object | Handled automatically by default. |

```json
{
    "locations": ["birmingham"],
    "listingType": "for-sale",
    "minBedrooms": 2,
    "maxPrice": 300000,
    "sortBy": "lowestPrice",
    "maxItems": 50
}
```

#### Output

One row, from a platform run on 2026-11-11 (location `london`, for sale; the summary and image URL are shortened). A field the listing does not give is `null`: this run's 10 rows were all new-home listings and none gave bathrooms, floor area or a price reduction:

```json
{
  "id": "74739897",
  "url": "https://www.zoopla.co.uk/new-homes/details/74739897/",
  "listingType": "for-sale",
  "title": "1 bed flat for sale",
  "address": "West Ham Village, 10 Avery Avenue, London E16",
  "propertyType": "flat",
  "bedrooms": 1,
  "bathrooms": null,
  "livingRooms": null,
  "sizeSqft": null,
  "price": 340000,
  "priceCurrency": "GBP",
  "priceLabel": "£340,000",
  "priceUnit": null,
  "pricePerWeek": null,
  "priceReduction": null,
  "publishedDate": "2026-11-11",
  "publishedLabel": "11th Nov 2026",
  "flag": "Just added",
  "underOffer": false,
  "availableFrom": null,
  "availableFromDate": null,
  "isNewHome": true,
  "tags": [
    "New home",
    "Shared ownership"
  ],
  "featureTags": [
    "garden",
    "modern"
  ],
  "summary": "A new collection of 1 & 2-bed Shared Ownership apartments - Enjoy Zone 2 living, minutes f...",
  "agentName": "Peabody - West Ham Village",
  "agentPhone": "020 3641 7249",
  "agentId": 156765,
  "agentUrl": "https://www.zoopla.co.uk/new-homes/developers/branch/peabody-west-ham-village-london-156765/",
  "imageUrl": "https://lid.zoocdn.com/645/430/1f320dadfa239c07e5aa9dcdce69f05b6d11894c.jpg",
  "imageCount": 26,
  "floorPlanCount": 1,
  "videoCount": 1,
  "latitude": 51.5199657823129,
  "longitude": 0.0121355986394558,
  "isPremium": true,
  "displayType": "premium",
  "location": "london",
  "searchUrl": "https://www.zoopla.co.uk/for-sale/property/london/",
  "scrapedAt": "2026-11-11T06:42:11+00:00"
}
```

### Notes and limits

- **At most 1,000 listings per search.** Zoopla serves 40 pages of 25 listings for any search,
  however many results it reports. For more, split an area into several locations or price bands.
- **`price` is what Zoopla shows as the price.** For auction listings it is the guide price.
  On rent searches it is the monthly rent (`priceUnit` `pcm`, or `pppm` per person for shared
  accommodation); `pricePerWeek` carries the weekly rent. A listing with no bedroom, bathroom or
  floor-area figure has `null` there, never 0.
- **Paid placements are not repeated.** Zoopla shows the same three featured listings above every
  results page; the Actor returns the regular results only, so each page adds 25 new listings.
- **New homes** carry `isNewHome` true and a link under `/new-homes/`.
- **Agent names and phone numbers are returned** as the listing shows them.
- Listings are read from the search results; the full description, tenure and EPC on the
  listing page are not fetched.
- Proxy routing is handled automatically. A page can take about 12 seconds to a minute, so a run with many pages is slow; the page cap and `maxItems` bound it.
- Reads the same public pages a browser does. See the disclaimer at the top.

### How much does it cost to scrape Zoopla?

Pay-per-event: you pay per result written to the dataset, with no subscription. The current
price is on the Actor's Pricing tab.

### FAQ

**How do I find a location slug?** Search on zoopla.co.uk and copy the part of the URL after
`/property/`, for example `manchester` from `/to-rent/property/manchester/`. If a slug is not
recognised the run ends with "location not found" instead of returning nothing quietly.

# Actor input Schema

## `locations` (type: `array`):

Zoopla location slugs to search, one search per entry, for example london, manchester or leeds. Copy the slug from a zoopla.co.uk search URL (zoopla.co.uk/for-sale/property/<slug>/). A slug the site does not know ends the run with a 'location not found' message.

## `listingType` (type: `string`):

Search properties for sale or to rent.

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

Only listings with at least this many bedrooms. Leave empty for no limit.

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

Lowest price in pounds: the asking price when for sale, the monthly rent when to rent. Leave empty for no limit.

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

Highest price in pounds: the asking price when for sale, the monthly rent when to rent. Leave empty for no limit.

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

Order of the results. Recommended is Zoopla's default; lowest price lists the cheapest first.

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

Stop after this many listings across all locations. Zoopla serves at most 40 pages of 25 listings (1,000) per search, so split a larger area into several locations or price bands.

## `maxPagesPerLocation` (type: `integer`):

Stop a location after this many result pages (25 listings each, 40 at most). Leave empty to read until Maximum listings or the last page.

## `proxyConfiguration` (type: `object`):

Proxy settings. Handled for you by default.

## Actor input object example

```json
{
  "locations": [
    "london"
  ],
  "listingType": "for-sale",
  "sortBy": "recommended",
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Items scraped from the target website.

# 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 = {
    "locations": [
        "london"
    ],
    "maxItems": 25,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "GB"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("reventadata/zoopla-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 = {
    "locations": ["london"],
    "maxItems": 25,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("reventadata/zoopla-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 '{
  "locations": [
    "london"
  ],
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}' |
apify call reventadata/zoopla-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reventadata/zoopla-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/xQRcrWSgCSdfLdazO/builds/wpW92JJXqdkZdUOsn/openapi.json
