Properstar - Global Property Listings, URLs & Agency Data avatar

Properstar - Global Property Listings, URLs & Agency Data

Pricing

from $1.50 / 1,000 results

Go to Apify Store
Properstar - Global Property Listings, URLs & Agency Data

Properstar - Global Property Listings, URLs & Agency Data

Extract property listings from Properstar across global domains like .com, .co.uk, .ch, and .fr. Use search pages, listing URLs, or agency pages. Returns prices, multilingual descriptions, photos, floor plans, GPS coordinates, and agency details via Properstar’s data service.

Pricing

from $1.50 / 1,000 results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

4

Total users

1

Monthly active users

2 days ago

Last modified

Share

Properstar Scraper: Listings, Prices, Photos & Agency Data

Properstar Scraper turns Properstar into structured property data across every country domain it runs on (.com, .co.uk, .ch, .fr, and more), auto-detected from the URL you paste. Paste a search-results page, a single listing page, or an agency or agent page, and get clean records with prices, multilingual titles and descriptions, photos, floor plans, GPS coordinates, energy ratings, and agency contact details. Switch on full detail for amenities, view types, construction year, and floor, then export to JSON, CSV, or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • Any Properstar country domain, auto-detected from the URL you paste, no domain allowlist to work around.
  • Three input shapes in one actor: search-results pages, single listing pages, and agency or agent pages.
  • Multilingual titles and descriptions captured per language, not just one.
  • 50+ named fields per listing, plus the complete source object so nothing is lost.
  • Optional full detail mode adds amenities, AI-extracted view types, construction year, and floor to search and agency results (listing-page URLs always include these).
  • Full measurements: living and land area in both square metres and square feet, alongside rooms, bedrooms, bathrooms, and energy/CO2 ratings.
  • Built for monitoring: incremental mode returns only new and changed listings on recurring runs, and a fully rejected run fails loudly instead of returning an empty dataset.

Use Cases

  • Real estate portals and PropTech tools: aggregate live listings across multiple country domains into one structured feed.
  • Investors and relocation agencies: compare prices, sizes, and locations across countries and currencies from a single search.
  • Market researchers and analysts: track price and inventory changes over time with incremental monitoring.
  • Lead generation and CRM tools: pull agency and agent contact details tied to their current live inventory.
  • Content and comparison sites: build country or city listing pages from live search results.

Data You Get

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

FieldExample
id"100000001"
url"https://www.properstar.co.uk/listing/100000001"
class"PropertyListing" (also "ProjectListing", "UnitListing" on agency/agent inventory)
transactionType"Sell" (also "Rent")
type / subType"Apartment" / "Penthouse"
title"Sample listing title"
description"Full description text appears here."
titleByLanguage / descriptionByLanguage{ "en": "Sample listing title" }, one entry per language
price / currency1000000 / "GBP"
livingAreaSqm / landAreaSqm120 / 0
livingAreaSqft / landAreaSqft1291 / 0
rooms / bedrooms / bathrooms4 / 3 / 2
floor / constructionYear"3" / 2020 (full detail mode)
address / city / postcode"Sample Street 1" / "London" / "SW1"
countryISO"GB"
latitude / longitude51.5000 / -0.1200
pictures / picturesCount["https://images.example.com/0001.jpg"] / 12
floorPlans["https://images.example.com/plan-0001.jpg"]
amenities / views["Balcony", "Parking"] / ["City"] (full detail mode)
status"Published"
energyRating / co2Rating"C" / "B"
agencyId / agencyName1000000 / "Sample Agency"
publicationDate"2026-01-01T00:00:00Z"
changeType / changedFields"NEW" / ["price"] (incremental mode only)

Every record also carries a typed set of extra fields (placeId, terraceAreaSqm, pricePerSquareMeter, areaValues, pictureItems, mediaItems, advertiser). advertiser is the advertising agency (id, name, originalId (the site's own account reference, not a URL slug), type) and pricePerSquareMeter is the price divided by the living area (empty when either is missing). updateDate and terraceAreaSqm are filled only when the site supplies them: updateDate comes with Fetch full detail, and terraceAreaSqm only when a listing declares a terrace area. The output dataset schema declares all fields. The Storage tab has three views: overview, changes (changeType, changedFields, first/last seen) and detail (amenities, views, floor, construction year). In incremental mode the record kinds are NEW, UPDATED, REAPPEARED, plus optional UNCHANGED and EXPIRED.

Every record also carries agencySlug, agencyType, and agencyStatistics for the listing's agency; firstSeenAt and lastSeenAt in incremental mode; and the complete source object as raw, so nothing from Properstar's own data is lost.

How to Use

  1. Paste one or more Properstar URLs into Properstar URLs: search-results pages, single listing pages, or agency/agent pages, from any country domain.
  2. For search-page URLs, optionally refine with Transaction type, Property types, price and bedroom range, or Luxury listings only.
  3. Turn on Fetch full detail if you want amenities, view types, construction year, and floor on search or agency results (listing-page URLs always include these).
  4. Set Max listings and Max pages per URL to control run size and cost, then click Start.

Default URLs: Properstar URLs has a non-empty default. If an API call omits startUrls, or you leave the prefilled list untouched in the Console, the actor works through these 10 example URLs (one per supported URL shape) in order until Max listings is reached. Max listings counts across all URLs, so with its default of 20 only the first search is usually reached; raise it (or set 0) to reach the later shapes:

  1. a city buy search
  2. the same search with a page number (?p=2)
  3. a city rent search
  4. a country-level buy search
  5. a search with price and bedrooms options
  6. a search with a sort option
  7. a search without a property-type segment (redirected by the site to the default type)
  8. a search on an alternate region (Switzerland)
  9. a single listing page (/listing/<id>)
  10. an agency page (/agency/<slug>/<id>)

Replace the list with your own URLs to scrape only those. Pasted-URL options: a page number (?p=2) starts the walk at that page (such a run never marks listings EXPIRED, see Resume and recurring updates), price (for example price=500000-1000000) and bedrooms (for example bedrooms=2) options are applied as filters on top of the page's own filters (the site's price filter compares euro-equivalent prices, so listings priced in another currency can sit slightly outside the stated range), and a sort option is ignored with a warning in the run log (the site's default order is used).

Basic search page:

{
"startUrls": ["https://www.properstar.co.uk/united-kingdom/london/buy/apartment-house"],
"maxItems": 50
}

Search page with refinement filters:

{
"startUrls": ["https://www.properstar.com/spain/barcelona/buy/apartment-house"],
"transactionType": "buy",
"propertyTypes": ["apartment"],
"minPrice": 300000,
"maxPrice": 800000,
"minBedrooms": 2,
"maxItems": 200
}

Single listing (always full detail):

{
"startUrls": ["https://www.properstar.co.uk/listing/100000001"]
}

Agency or agent inventory:

{
"startUrls": ["https://www.properstar.co.uk/agency/sample-agency/1000000"],
"maxItems": 100,
"enrichDetail": true
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/properstar-scraper").call(run_input={
"startUrls": ["https://www.properstar.co.uk/united-kingdom/london/buy/apartment-house"],
"maxItems": 50,
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
print(listing["title"], listing["price"], listing["currency"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/properstar-scraper').call({
startUrls: ['https://www.properstar.co.uk/united-kingdom/london/buy/apartment-house'],
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.

Caps and cost controls

  • Max listings (maxItems) stops the run once this many listings, across all URLs, have been returned. 0 is unlimited.
  • Max pages per URL (maxPagesPerUrl) caps how many 25-listing pages are walked per search or agency URL. Leave it empty (0) to walk every page until the site's own end of results, capped at roughly 2000 retrievable listings per URL.
  • Residential traffic budget (maxResidentialMb) caps the residential-exit browser traffic used for connection setup and, when Fetch full detail is on, per-listing detail fetches. The budget is estimated per page opened, not metered byte for byte. Once spent, the run keeps going and finishes the already-cheap list pagination, but stops fetching further detail pages. 0 means no cap.

Known limitation: overlapping URLs end early. "Already collected" is checked against everything collected so far in the whole run, not just the current URL. Once a later search or agency URL has returned at least one new listing, a following page of that URL made up only of listings an earlier URL already returned is read as the end of its results, and its deeper pages are silently skipped with no error. Example: you run a London search and then a Westminster search in one run; page 1 of Westminster has a few new listings, but every listing on page 2 was already returned by London, so the actor never reads page 3 of Westminster, even if it holds listings London never showed. The first URL in a run is never affected, and a later URL whose opening pages are all duplicates keeps going. The same rule applies to a Resume run: if new listings have been added near the top of the results since the interrupted run, the pages that follow them can consist entirely of already-saved listings, and the walk stops there before reaching the listings it was meant to add. If your URLs overlap and you need every listing, run each one as its own run (or its own scheduled task); for Resume, resume soon after the interruption.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted full-catalogue pull in a brand-new run: paste a previous run ID or dataset ID, and this run skips every listing id already saved there, appending only new listings. A checkpoint is also saved automatically as the run progresses, so an Apify platform migration or a Resurrect of a failed run picks back up in the same run without re-saving already-collected listings. No input is needed for that part; it is automatic.
  • Incremental mode (incrementalMode) is a different workflow: it re-runs the same startUrls and filters on a schedule (for example daily) and tells you only what changed, using state the actor keeps for you. The first run for a given search returns every listing as NEW. Later runs return only NEW, UPDATED (with changedFields), and REAPPEARED listings by default. Turn on emitUnchanged to also get UNCHANGED rows, or emitExpired to also get EXPIRED rows for listings that disappeared. EXPIRED detection only fires once a run has fully scanned every tracked URL; it is skipped on a resumeFromRunId run, a run capped by Max listings, a run where any URL stopped at Max pages per URL, a URL with more results than the roughly 2000 the site lets you retrieve, a run where any start URL carries a page number (?p=2 starts mid-catalogue, so earlier pages were not scanned), or a run where a page or connection was rejected mid-scan, since none of those prove a listing is actually gone. A listing marked EXPIRED that shows up again later is returned as REAPPEARED; without emitExpired, listings are never marked gone, so REAPPEARED does not occur.
  • Known limitation: false EXPIRED rows with overlapping URLs. A URL that stops early because of the overlap rule above (a page holding only listings an earlier URL already returned) still counts as having reached the end of its results. So with emitExpired on, listings on that URL's skipped deeper pages can be marked EXPIRED (and billed) while they are still live. Example: you track a London search and a Westminster search together, and a flat appears only on page 3 of Westminster. On a later run, page 2 of Westminster holds only listings London already returned, the walk stops there, and that flat comes back as EXPIRED although nothing on Properstar changed; it returns as REAPPEARED the next time a run reaches it. If you rely on EXPIRED, track each area or filter set in its own run (each gets its own saved state automatically), and treat an EXPIRED row as "not seen this run" rather than proof of delisting.
  • What counts as a change: every returned field except raw is compared (fields added in later versions, such as advertiser and pricePerSquareMeter, are compared from the second run after they first appear, so rolling them out never causes a wave of UPDATED rows; updateDate is compared once the detail page has supplied it). raw (the complete source object) is always excluded from the comparison because it carries per-search ranking scores and per-viewer notification flags that change between runs while the listing itself does not; it is still returned in full on every row. Add your own exclusions with ignoreFieldsForChanges; adding a field there never causes a one-time wave of UPDATED rows.
  • Incremental mode and Resume can be combined only to seed a brand-new baseline from an earlier dataset. If saved incremental state already exists for the same search, a run with both set fails straight away with a message; remove resumeFromRunId or set a different stateKey.
  • State is namespaced automatically per distinct startUrls + filter + enrichDetail combination, so two different searches never mix baselines. Set stateKey to name a campaign explicitly, or to share state deliberately across a different configuration.

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
startUrlsarrayrequiredProperstar search, listing, or agency/agent URLs from any country domain. Multiple URLs are processed in order.
transactionTypestringanyRefine search-page URLs to buy or rent. any keeps the URL's own setting.
propertyTypesarray[]Restrict to apartment, house, land, commercial, office, parking, building, or room. Leave empty to use the URL's own types.
minPriceinteger(none)Minimum price in the listing's currency.
maxPriceinteger(none)Maximum price in the listing's currency.
minBedroomsinteger(none)Minimum number of bedrooms.
maxBedroomsinteger(none)Maximum number of bedrooms.
luxuryOnlybooleanfalseReturn only listings flagged as luxury.
enrichDetailbooleanfalseFetch each listing's detail page for amenities, AI-extracted view types, construction year, and floor. Adds a per-listing surcharge and is slower. Listing-page URLs always include full detail regardless of this setting.
maxItemsinteger20Maximum listings to return across all URLs. 0 = unlimited.
maxPagesPerUrlinteger0Maximum result pages to walk per search/agency URL (25 listings per page). 0 walks every page until the site's own end of results, capped at roughly 2000 retrievable listings per URL.
maxResidentialMbinteger0Soft, estimated cap on residential-exit browser traffic (connection setup, plus per-listing detail fetches when enrichDetail is on). Once reached, detail fetches stop for the rest of the run; list pagination is unaffected. 0 = no cap.
proxyobjectResidentialConnection settings. The one-time setup needs a residential exit (the default); pick a country to pin the exit region, or leave it unset to rotate.
resumeFromRunIdstring(empty)A previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already collected there (matched by id) are skipped, so this run only saves the new ones.
incrementalModebooleanfalseTurn on for recurring monitoring of the same startUrls/filters. Only NEW, UPDATED, and REAPPEARED listings are returned after the first run.
stateKeystring(empty)Name an incremental-mode monitoring campaign, or share state deliberately across differently-configured runs. Auto-derived from startUrls + filters + enrichDetail when empty.
emitUnchangedbooleanfalseIncremental mode only. Also return (and bill) listings with no change, marked UNCHANGED.
emitExpiredbooleanfalseIncremental mode only. Also return (and bill) listings no longer found, marked EXPIRED, only when the run fully scanned every tracked URL. See "Resume and recurring updates" for the exceptions and a known limitation with overlapping URLs.
ignoreFieldsForChangesstring list(empty)Incremental mode only. Field names (or dotted paths such as agencyStatistics.listingCount) that do not count as a change: they are left out of the comparison and out of changedFields, but are still returned in every row. Exact-name match.
mcpConnectorsarray(none)Optional: send a summary of each record to apps you authorized under Integrations.
notionParentPageUrlstring(none)Notion connector only: page under which records 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 listing.

{
"id": "100000001",
"url": "https://www.properstar.co.uk/listing/100000001",
"class": "PropertyListing",
"transactionType": "Sell",
"type": "Apartment",
"subType": "Penthouse",
"title": "Sample listing title",
"description": "Full description text appears here.",
"titleByLanguage": { "en": "Sample listing title" },
"descriptionByLanguage": { "en": "Full description text appears here." },
"price": 1000000,
"currency": "GBP",
"livingAreaSqm": 120,
"livingAreaSqft": 1291,
"rooms": 4,
"bedrooms": 3,
"bathrooms": 2,
"floor": "3",
"constructionYear": 2020,
"address": "Sample Street 1",
"city": "London",
"postcode": "SW1",
"countryISO": "GB",
"latitude": 51.5,
"longitude": -0.12,
"pictures": ["https://images.example.com/0001.jpg"],
"picturesCount": 12,
"floorPlans": ["https://images.example.com/plan-0001.jpg"],
"amenities": ["Balcony", "Parking"],
"views": ["City"],
"agencyId": 1000000,
"agencyName": "Sample Agency",
"agencySlug": "sample-agency",
"agencyStatistics": { "numberOfListings": 0 },
"publicationDate": "2026-01-01T00:00:00Z"
}

Plan Requirement

Properstar protects its pages with a browser challenge that a plain HTTP request cannot pass from any connection type (direct, datacenter or residential); only a real browser that waits a few seconds clears it. The actor therefore opens a browser for a one-time setup, and the default Connection setting selects a residential exit for it; a different setting may return no listings. Once setup is complete, the bulk of the data pull automatically switches to a lower-cost connection pool. If the challenge does not clear in time, the run retries on a fresh connection and fails with a clear message when every attempt is rejected. Pick a proxy country under Connection to pin the exit region, or leave it unset to rotate automatically.

FAQ

How much does it cost?

Charges follow what the actor delivers: you pay per listing returned (the result event, one per row saved to the dataset), and the Actor start event is charged when the run starts (it is based on the run's memory, so it can be more than one unit per run). When Fetch full detail is switched on, the detail-enrichment event is charged once per listing, at the moment that enriched listing is saved to the dataset, and only for enriched listings that are actually saved to the dataset (never for rows that are suppressed, such as UNCHANGED rows when Emit unchanged is off, and never for deduplicated or failed rows; an UNCHANGED row returned because Emit unchanged is on is charged like any other enriched row). In incremental mode, UNCHANGED and EXPIRED rows are returned and billed only when you turn on Emit unchanged or Emit expired. The Pricing tab shows the current rates. Use Max listings and Max pages per URL to cap the cost of any run.

This actor collects only publicly available listing data. You are responsible for how you use it: follow Properstar's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Photos, floor plans, and agency contact details may be subject to third-party rights.

Can I monitor the same search for new or changed listings?

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

Why did my run return fewer listings than I expected?

Check Max listings and Max pages per URL, which both cap a run by design. A single search or agency URL is also capped at roughly the site's own 2000-listing retrievable ceiling. If you pass several overlapping URLs in one run, a later URL can also stop early once a whole page of it repeats listings an earlier URL already returned (see "Known limitation: overlapping URLs end early" under Caps and cost controls), and with Emit expired on, listings it skipped this way can come back as EXPIRED even though they are still live. Run overlapping areas or filters as separate runs to avoid both.

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

If every request to Properstar was rejected, the run fails with a clear message so "no listings match" is never confused with "the connection could not be established". Run it again in a few minutes. If only some of your URLs failed, you still get what was found from the others.

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.

🔗 Want more real estate data?

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

🏠 Immowelt.de Scraper
Scrape detailed property listings from Immowelt.de into clean structured data. Extract...
🏠 SeLoger Scraper
Scrape SeLoger.com properties for sale and rent. Search by location with price, room and...
🏠 Domclick RU Property Scraper
Extract property listings from domclick.ru, one of Russia’s largest real estate portals...
🏠 Fotocasa.es
Scrape Spain property listings from fotocasa.es. Get price, surface, rooms, baths...
🏠 Funda in Business Scraper
Scrape Funda in Business commercial listings: offices, retail, industrial, catering and...
🏠 Zonaprop Argentina
Scrape Zonaprop.com.ar property listings across Argentina. Extract 140+ fields including...

👉 Browse all abotapi scrapers

💬 Support

  • 🐞 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 contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.