Zoopla Scraper - Property Listings, Sold Prices & Monitoring avatar

Zoopla Scraper - Property Listings, Sold Prices & Monitoring

Pricing

from $1.00 / 1,000 property results

Go to Apify Store
Zoopla Scraper - Property Listings, Sold Prices & Monitoring

Zoopla Scraper - Property Listings, Sold Prices & Monitoring

Scrape UK property from Zoopla: for sale, to rent and Land Registry sold prices. Search by place with filters or paste links. Optional full detail adds EPC, floor plans, tenure, price history, stations and agent phone. Incremental mode for change monitoring.

Pricing

from $1.00 / 1,000 property results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Zoopla UK Property Scraper: For Sale, To Rent & Sold Price Data

Zoopla UK Property Scraper turns Zoopla into a structured UK property data feed. Pull live listings for sale, live listings to rent, and Land Registry sold-price records, searched by place name with Zoopla's own filters or scraped directly from links you paste. Turn on full listing detail to add EPC rating, floor plans, tenure, council tax band, service charge, UPRN, price history and the agent's phone number, then export to JSON, CSV or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • Three channels, one actor. For sale, to rent, and Land Registry sold prices, the same UK place search covers all three.
  • Search by place or paste links. Look up a city, postcode or district with Zoopla's own filters, or scrape the exact results page, house-prices page or listing page you already have.
  • Full listing detail on demand. Switch on EPC rating and graph images, floor plans, tenure, council tax band, service charge, ground rent, UPRN, price history, nearest stations, EV charge points, virtual tours, brochures and the agent's phone number, billed only per listing you actually enrich.
  • UK-specific fields throughout. Postcode, outcode, UPRN, council tax band and EPC rating are first-class fields, and every price is in GBP.
  • Built for monitoring. Incremental mode returns only new, updated and reappeared listings on a recurring search, so a daily run does not rebill what has not changed.
  • Resume without repaying. Continue an interrupted crawl from its run id and skip everything it already collected.
  • A refused run fails loudly. If Zoopla refuses every request, the run stops with a clear message instead of returning an empty dataset that looks like "no results".

Use Cases

  • Property investment and comparables: compare current asking prices against Land Registry sold prices for the same postcode to judge whether a listing is fairly priced.
  • PropTech and market research: build historical datasets of prices, EPC ratings, tenure and property types across areas for analysis or machine learning.
  • Listing and price-drop monitoring: run incremental mode daily to catch newly listed, reduced, let-agreed or sold properties in a target area.
  • Agent and branch tracking: monitor a specific branch's advertised properties by pasting its results pages, or track how a listing's price changes over time.
  • Relocation and house-hunting tools: feed a personal search (place, budget, bedrooms, property type) into a spreadsheet, bot or app.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
listingId"10000001"
listingType / channel"sale" / "for-sale" (also "rent" / "to-rent", "sold" / "sold")
urlZoopla link to the listing or sold record
title / address"2 bed flat for sale" / "Example House, Sample Street, Manchester M1"
price / priceLabel / currency285000 / "£285,000" / "GBP"
bedrooms / bathrooms / receptionRooms2 / 1 / 1
propertyType / sizeSqft"flat" / 720
tenure"leasehold"
epcRating / councilTaxBand"D" / "B" (full detail only)
serviceCharge / groundRent"£1,200 per year" / "£100 per year" (full detail only)
chainFreetrue
uprn"0000000000"
postcode / latitude / longitude"M1 0AA" / 53.48 / -2.24
nearestStations[{ "name": "Example Street", "miles": 0.4 }] (full detail only)
features["Private balcony", "Allocated parking", "Open-plan kitchen"] (full detail only)
images / floorPlanImages / epcImageslisting photo, floor plan and EPC graph URLs (full detail only)
branchName / branchPhone"Example Estates" / "01000 000000"
lastSoldPrice / lastSoldDate210000 / "2018-03-22" (sold channel)
publishedOn"2026-09-12T08:30:00"
detailScrapedtrue
changeType"NEW" (also "UPDATED", "UNCHANGED", "REAPPEARED", "EXPIRED"; incremental mode only)
scrapedAt"2026-09-30T11:00:00Z"

Sold records carry uprn, address, postcode, latitude, longitude, lastSoldPrice, lastSoldDate, bedrooms, bathrooms, receptionRooms, propertyType, tenure and floorAreaSqM; they have no listing page, so full detail adds nothing to them. Full detail also adds commonholdDetails, materialInformation, broadband, parkingInfo, accessibility, evChargingStations, virtualTours, brochures, priceHistory and smartTags to sale and rent records.

How to Use

  1. Pick a mode: search (by place name, with filters) or url (paste links you already have).
  2. In search mode, name your places to search and the channels (for sale, to rent, sold prices), then set price, bedroom, property type and other filters.
  3. Turn on Fetch full listing details if you need EPC, tenure, price history and the rest, and set Max results to control run size and cost.
  4. Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.

Search by place, with filters:

{
"mode": "search",
"locations": ["London", "Manchester"],
"channels": ["for-sale"],
"minPrice": 200000,
"maxPrice": 500000,
"minBeds": 2,
"propertySubTypes": ["flats"],
"includeDetails": true,
"maxItems": 50
}

Places can be a city (Manchester), a postcode area (SW19), or a district written the way you would say it (Islington, London).

Paste links:

{
"mode": "url",
"startUrls": [
"https://www.zoopla.co.uk/for-sale/property/london/islington/",
"https://www.zoopla.co.uk/house-prices/london/islington/"
],
"includeDetails": true
}

A results page returns its listings, a house-prices page returns sold records, and a single listing link returns that one property with full detail, whether or not Fetch full listing details is on, so a pasted listing link that is read successfully always carries the full-detail charge. A page number in a pasted address is used as the starting page.

Sold prices only, last 12 months:

{
"mode": "search",
"locations": ["Islington, London"],
"channels": ["sold"],
"soldPeriodMonths": "12",
"maxItems": 100
}

Daily rental monitoring with incremental mode:

{
"mode": "search",
"locations": ["Manchester"],
"channels": ["to-rent"],
"incrementalMode": true,
"emitExpired": true,
"maxItems": 200
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/zoopla-co-uk-scraper").call(run_input={
"mode": "search", "locations": ["London"], "channels": ["for-sale"], "maxItems": 50
})
for record in client.dataset(run["defaultDatasetId"]).iterate_items():
print(record["address"], record["priceLabel"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/zoopla-co-uk-scraper').call({
mode: 'search', locations: ['London'], channels: ['for-sale'], maxItems: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

How maxPages and maxItems shape a run

Zoopla serves at most 40 result pages per search, about 1000 records. To go deeper, split the search: narrow by price band, bedroom count, property type, or search smaller places. Premium listings are pinned to the top of the first page whatever sort order you pick, which is Zoopla's own behaviour, not the actor's.

In incremental mode, treat Max results as a cap on how many listings are scanned, not a guarantee of how many new or changed rows you get back. The actor stops turning pages once it has read Max results listings, and only then drops the ones that turn out unchanged. So if the first listings Zoopla shows are ones you already have, they can use up the whole cap: the run can return zero rows and report "nothing changed since the last run" while genuinely new listings sit further down the same page or on later pages. A new listing that was read past the cap this way is also skipped if it shows up again under a later place in the same run. Because the same unchanged listings come first again next time, this repeats on every scheduled run until you change the setup. For monitoring, set Max results well above the number of listings the search shows (or 0 for no cap), or sort by newest listings so new ones are read first.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips every listing already collected there, so you don't pay twice. It cannot be combined with incremental mode once that search already has saved state; use it to bootstrap a brand-new monitoring campaign instead. If you do bootstrap that way, the listings skipped from the earlier run are tracked by id only, so the first normal incremental run that sees each of them again returns, and bills, it once as UPDATED with every field listed in changedFields, even if nothing changed on Zoopla. From the run after that they behave normally. A skipped listing that disappears before it is seen again is not reported as EXPIRED.
  • Incremental mode (incrementalMode) is for running the same search again and again, for example daily. The first run returns everything as NEW. Later runs normally return only NEW, UPDATED (with changedFields) and REAPPEARED records. UNCHANGED is suppressed and not billed unless emitUnchanged is on. EXPIRED is only produced after a run that scanned the whole tracked search, and only with emitExpired on. stateKey names or shares the stored state.

Note on emitExpired and Max results: the EXPIRED batch is computed, pushed and billed as one unit once a run has fully scanned its tracked search, and it is not trimmed against Max results. If a search has accumulated a large tracked baseline and many of those listings have genuinely disappeared since the last run, a capped run can still return, and bill, more EXPIRED rows than Max results allows. Turn emitExpired off, or budget for it separately, on searches with a large tracked history.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch to look up places by name and apply filters, or url to scrape Zoopla links you paste.
locationsarray["London"]One or more UK places, for example London, Manchester, SW19, or Islington, London. Search mode only.
channelsarray["for-sale"]Any of for-sale, to-rent, sold. Search mode only.
minPriceinteger(none)Lowest price to include, in GBP.
maxPriceinteger(none)Highest price to include, in GBP.
minBedsinteger(none)Only properties with at least this many bedrooms.
maxBedsinteger(none)Only properties with at most this many bedrooms.
minBathsinteger(none)Only properties with at least this many bathrooms.
propertySubTypesarray(none, every type)Detached, semi-detached, terraced, flats, bungalows, farms and land, park homes, offices, retail, hospitality, industrial. On the sold channel only the first type picked is used.
radiusstring0Widen the search around the place, from This area only up to 40 miles.
addedSincestring(any time)Last 24 hours, 3 days, 7 days or 14 days.
keywordsstring(none)Free-text keyword, for example garden, garage or balcony.
sortBystringrecommendedRecommended, newest, highest price, lowest price, most reduced. Premium listings stay pinned to the top regardless.
chainFreeOnlybooleanfalseFor sale only. Only chain-free properties.
auctionOnlybooleanfalseFor sale only. Only auction properties.
sharedOwnershipOnlybooleanfalseFor sale only. Only shared-ownership properties.
retirementOnlybooleanfalseFor sale only. Only retirement properties.
reducedPriceOnlybooleanfalseOnly listings whose asking price has been reduced.
includeUnderOfferbooleanfalseFor sale only. Also return properties under offer or sold subject to contract, hidden by Zoopla by default.
includeLetAgreedbooleanfalseTo rent only. Also return properties let or let agreed, hidden by Zoopla by default.
furnishedStatestring(any)To rent only. Furnished, part furnished, or unfurnished.
priceFrequencystring(Zoopla default, per month)To rent only. Whether minPrice/maxPrice are per month or per week.
petsAllowedbooleanfalseTo rent only. Only rentals that accept pets.
billsIncludedbooleanfalseTo rent only. Only rentals with bills included.
soldPeriodMonthsstringall-timeSold prices only. How far back to look for sold records.
startUrlsarraysampleZoopla results pages, house-prices pages, listing pages, or bare listing ids. URL mode only; filters above are not applied here.
includeDetailsbooleanfalseAdd the full detail set to every sale/rent record: EPC, floor plans, tenure, council tax band, service charge, ground rent, UPRN, price history, description, features, nearest stations, EV points, virtual tours, brochures and the agent's phone number. Billed as a per-listing surcharge; no effect on the sold channel.
maxItemsinteger20Maximum records across the whole run. 0 means unlimited.
maxPagesinteger(none, every page)Maximum result pages per place and channel. Empty or 0 covers every page Zoopla offers, at most 40 per search.
resumeFromRunIdstring(none)Continue one interrupted run without returning or paying for rows it already collected.
incrementalModebooleanfalseTurn on for daily or recurring monitoring; later runs return only NEW, UPDATED and REAPPEARED records.
stateKeystring(none)Name a monitoring campaign, or deliberately share state between runs.
emitUnchangedbooleanfalseAlso return, and bill, UNCHANGED records.
emitExpiredbooleanfalseAlso return, and bill, EXPIRED records, only after a run that fully scanned the tracked search.
proxyobjectApify ProxyConnection settings. A proxy connection is required for reliable results.
mcpConnectorsarray(none)Optional: send a summary of each record to apps you authorized under Integrations.
notionParentPageUrlstring(none)Notion connector only: page under which item pages are created.
maxNotifyListingsinteger50Cap on items written to each connector per run. Does not affect the dataset.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"listingId": "10000001",
"listingType": "sale",
"channel": "for-sale",
"url": "https://www.zoopla.co.uk/for-sale/details/10000001/",
"title": "2 bed flat for sale",
"address": "Example House, Sample Street, Manchester M1",
"price": 285000,
"priceLabel": "£285,000",
"currency": "GBP",
"bedrooms": 2,
"bathrooms": 1,
"receptionRooms": 1,
"propertyType": "flat",
"sizeSqft": 720,
"tenure": "leasehold",
"epcRating": "D",
"epcImages": ["https://lid.zoocdn.com/1024/768/example-epc.png"],
"floorPlanImages": ["https://lid.zoocdn.com/1024/768/example-floorplan.jpg"],
"councilTaxBand": "B",
"serviceCharge": "£1,200 per year",
"groundRent": "£100 per year",
"chainFree": true,
"uprn": "0000000000",
"postcode": "M1 0AA",
"latitude": 53.48,
"longitude": -2.24,
"nearestStations": [{ "name": "Example Street", "miles": 0.4 }],
"features": ["Private balcony", "Allocated parking", "Open-plan kitchen"],
"priceHistory": [],
"images": ["https://lid.zoocdn.com/1024/768/example-photo.png"],
"branchName": "Example Estates",
"branchPhone": "01000 000000",
"branchUrl": "https://www.zoopla.co.uk/find-agents/branch/example-estates-1/",
"publishedOn": "2026-09-12T08:30:00",
"detailScraped": true,
"scrapedAt": "2026-09-30T11:00:00Z"
}

Plan Requirement

The default proxy setting works out of the box, and the actor never upgrades a run to a metered proxy pool on its own. For large or frequent runs, or if a run reports refused requests, switch to the residential proxy group under Connection.

FAQ

How much does it cost?

You pay per property record returned, with full listing detail billed only for listings actually enriched: when you switch it on, and for every listing link you paste in URL mode (those are always read in full). The Pricing tab shows the current rates. Use Max results to cap the cost of any run.

This actor collects only publicly available property data. You are responsible for how you use it: follow Zoopla's terms and the laws that apply to you, including UK GDPR where records carry an agent's contact details, and get legal advice if you plan commercial redistribution.

Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared records, and unchanged ones are not billed unless you turn emitUnchanged on.

Why did an incremental run come back with fewer rows than Max results?

Max results caps how many listings are scanned, not how many survive incremental filtering. Listings that turn out unchanged still use up the cap, and once it is used up no further listings or pages are read, so a run can even return zero rows while new listings exist further down the results, and the same thing happens again on the next run. Set Max results well above the number of listings your search shows (or 0), or sort by newest listings, for monitoring runs.

Why did every field show as changed on my first run after using Resume with incremental mode?

When a new incremental campaign is started with resumeFromRunId, the listings from that earlier run are recorded by id only, without their field values. On the next normal run each of them is returned once as UPDATED with every field in changedFields, and billed like any other row, even when nothing changed on Zoopla. It happens once per listing; later runs compare real values. To avoid it, start the campaign with a plain incremental run instead of a resume.

Why can an EXPIRED batch push more rows than Max results?

Once a run fully scans its tracked search, emitExpired computes and bills every listing that disappeared since the last run in one batch, without trimming it to Max results. On a search with a large tracked history, this can add more billed rows than Max results implies. Turn emitExpired off on very large monitoring campaigns if this matters to you.

Why did my run fail instead of returning an empty dataset?

If Zoopla refuses every request, the run stops with a clear message so "no listings match" is never confused with "nothing could be read". Try again, or switch the Connection section to a different proxy pool. If only some places or pages could be read, you still get what was found.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

🏠 Rightmove
From $1/1K. Fast, reliable Rightmove.co.uk scraper for sale, rent, and sold-price...
🏠 Jitty UK AI Property Scraper
Scrape residential property listings from jitty.com, extract properties across the UK...
🏠 OnTheMarket Scraper
Scrape onthemarket.com for-sale, to-rent and new-homes listings with 85+ fields: price...
💼 Reed.co.uk Scraper
Scrape Reed.co.uk jobs, companies, courses, and reviews. Search by keyword, location...
💼 NHS UK
From $0.8/1k. Scrape nhs.uk Jobs listings into a flat dataset. Extract 50+ fields...
🏠 iProperty MY Scraper
Scrape property listings from iProperty.com.my for sale or rent: price, PSF, bedrooms...

👉 Browse all abotapi scrapers

💬 Support & custom scrapers

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.