15% off your first purchase with code WELCOME15, up to 25 GB.

See pricing

Real estate & jobs · Real estate listings

Real estate listing scraping, city by city

Real estate listing scraping turns public property portals into a daily panel of asking prices, time on market and stock per city. Collect each city from an exit inside it, refresh the listings that changed, and keep the panel coherent enough to model on.

Pay per GB · No subscription · Prices in EUR, excl. VAT

Illustration. The URLs and values are fictional; the connection string is the real grammar.
€0.75
per GB on ARC Basic, excl. VAT
−21%
at 1 TB (€0.59 / GB)
185+
countries, city targeting
Never
expires — no subscription

Written for

  • Proptech data teams
  • Automated valuation model builders
  • Housing market researchers
  • Build-to-rent acquisition analysts

The short answer

  • A listing price is an asking price, not a transaction price. It leads official house price indices, which measure completed sales, but it is a different number and should be labelled as one.
  • Days on market is only as good as your refresh cadence: you measure it as the gap between the first and last day you saw a listing, so a missed run shortens or splits it.
  • Collect each city from an exit in that city. Portals default to the visitor's region, and a national view assembled from one location under-samples smaller markets.
  • Most listing pages are light HTML. Run the bulk on ISP proxies with city targeting, block images, and move only the portals that fail to a residential pool.
  • Relisting, template reuse and paused adverts create duplicates that look like new supply. Deduplicate on property features, not on the portal's listing ID.

The problem

What goes wrong without the right IPs

Relisted properties look like new supply

Agents take a listing down and put it back up, change the price, or reuse an old advert as a template. Count listing IDs and your new-supply series is inflated by the same flat several times. Researchers working with portal data classify these as duplicate spells for exactly this reason.

A missed run corrupts days on market

Time on market is the span between first and last sighting. If a run is throttled on a Tuesday and a flat sells on Wednesday, your record ends a day early, and if it comes back after a pause you may count it as two listings. Gaps are a measurement error, not just a delay.

Search results are capped and ranked locally

Portals cap how many result pages a search returns and rank by the visitor's region. A city-wide search that silently stops at page 50 misses the long tail. Split searches by district, price band or property type until each one returns fewer results than the cap.

Units, currency and templates change by locale

Square metres become square feet, cold rent becomes warm rent, and a field that is structured in one country is free text in the next. Collecting from the market's own locale keeps the template stable enough to parse.

Images are the bill

A listing page with its gallery is several megabytes; the same page without images is a fraction of that. Billing is per gigabyte, so a crawler that fetches every photo on every daily refresh pays for pictures that have not changed.

Agent contact details are personal data

Many listings carry an agent's or private seller's name and phone number. If you do not need them for your model, do not store them. If you do, you have the obligations that come with personal data.

What to collect

What you can collect

Asking price and price history
The core series. Store every price change with the date you first saw it; reductions are one of the earliest signs a local market is turning.
First seen / last seen dates
Your own observation window, from which days on market is derived. Keep it separate from any 'listed on' date the portal prints, which can reset on relisting.
Living area, rooms, plot area
The hedonic variables a valuation model needs, and the features you deduplicate on. Normalise units at collection, not at analysis time.
Location (postcode or district, coordinates where public)
Lets you aggregate to the geography your model uses. Many portals blur exact addresses for privacy; store what is shown, not what you infer.
Property type and tenure (sale or rent)
Flats for rent and houses for sale are different markets with different turnover. Mixing them in one supply count hides both trends.
Construction year, condition, energy rating
Explains price differences between otherwise similar homes. Store the energy rating as printed, with its scale, since scales differ by country.
Listing status (active, reserved, removed)
A listing marked reserved and later reactivated is a failed sale, not new supply. The status flag is how you tell them apart.
Agent or private seller flag
Private and agency listings price and sell differently. Store the flag; leave the person's name out unless you have a reason and a lawful basis to keep it.

How it runs

How a collection run works

  1. 01

    Partition each city until no search hits the cap

    Split every city into district × property type × price band searches, and check that each returns fewer results than the portal will page through. A partition that hits the cap is a partition you have to split again.

  2. 02

    Sweep result pages daily from inside the city

    Fetch the result pages through a city-targeted exit and extract listing IDs and the summary price. This is the cheap pass: it tells you what is new, what changed price and what disappeared, without opening every listing.

    City-targeted result sweep on ARC Basic
    import httpxPROXY_HOST, PROXY_PORT, PASSWORD = "proxy.arcproxies.io", 8000, "PASSWORD"def proxy_for(country: str, city: str) -> str:    # Rotating ISP pool, city token is the city name lowercased without spaces    user = f"USERNAME_pool-arc_country-{country}_city-{city}"    return f"http://{user}:{PASSWORD}@{PROXY_HOST}:{PROXY_PORT}"def sweep(search_url: str, country: str, city: str, pages: int):    with httpx.Client(proxy=proxy_for(country, city), timeout=30) as client:        for page in range(1, pages + 1):            r = client.get(search_url, params={"page": page})            if r.status_code == 429:                break  # back off and resume this partition later            yield page, r.textfor page, html in sweep("https://portal.example/de/berlin/flats-for-sale", "de", "berlin", 20):    ...  # extract listing id, price, area; store with today's date
  3. 03

    Fetch detail pages only for new or changed listings

    Open a listing's detail page when it first appears or when its summary price moves. Block images and fonts; fetch the gallery once, at first sighting, if your model uses it.

  4. 04

    Deduplicate on features, not IDs

    Match listings that share location, living area within a tolerance, room count and a similar price. Treat a match that reappears after a short gap as one spell, not two, and record the gap.

  5. 05

    Aggregate to an asking-price panel

    Compute median asking price per square metre, new listings, removals and median days on market per city and week. Label the series as asking prices so nobody downstream mistakes it for a transaction index.

Setup

Recommended setup

City targeting is the whole point here, so stay on the rotating pools: ARC Mobile has no city targeting.

PlanRoleSessionTargetingPrice
ARC Basic
Rotating ISP
Default for result pages and detail pages
Listing pages are mostly plain HTML. ARC Basic takes the city token at our lowest price per GB, which is where a daily refresh across many cities belongs.
RotatingCountry + city
€0.75 / GB
€0.59 at 1 TB (−21%)
ARC Fast
Rotating residential
For portals that need a consumer IP
Residential exits in the city itself. A sticky handle keeps one partition's pagination on one IP, so page 1 and page 20 come from the same visitor.
Sticky, 10 minutes per partitionCountry + city
€2.00 / GB
€1.12 at 1 TB (−44%)
ARC Ultra
Premium residential
For the one portal your model cannot do without
Premium residential for the source where a missed day is a hole in the panel. Route that portal only; leave the rest where they are cheaper.
Sticky per partitionCountry + city
€3.50 / GB
€2.17 at 1 TB (−38%)

Prices are list prices in EUR, excl. VAT. Volume discounts apply automatically at checkout — full ladder on the pricing page.

Connect

One connection string per market

The country, city and sticky session are tokens in the username. Change the token, change the market; keep the session handle, keep the IP.

sticky · Germany · 10 min
http://USERNAME_pool-arcfast_country-de_session-k3f9a1x2_sesstime-10:PASSWORD@proxy.arcproxies.io:8000

Swap _country-de for any market you quote. The full grammar is on the residential proxies page.

Illustration

What a daily listing panel across 12 cities costs

An illustration with round numbers — your pages, payloads and retry rate will differ. The arithmetic is the real price ladder.

Assumptions

Cities
12 (e.g. Berlin, Munich, Hamburg, London, Manchester, Paris …)
Result pages per city per day
400 at 120 KB
New or changed listings per city per day
600 detail pages at 180 KB
Images
blocked
Cadence
daily, 30 days
Bandwidth per month
≈ 57 GB

On ARC Basic

€41.61per month, excl. VAT

57 GB bought in one purchase at €0.73 / GB (−3% volume rung, applied automatically).

The same 57 GB on ARC Fast: €95.76 at €1.68 / GB. Route only the jobs that need it through the pricier pool.

Fetching every gallery image on every refresh would multiply this several times over. Fetch images once, at first sighting, and only for the listings your model keeps. See how to reduce proxy bandwidth.

Bandwidth never expires, so a bigger single purchase is only ever cheaper per GB. See the ladder.

In depth

Real estate listings, in depth

Asking prices vs transaction prices: what listing data measures

A listing scraper measures the offer side of the market: the price an owner or agent is willing to accept, the stock on offer, and how long it stays there. Official house price indices measure something else. Eurostat's house price index covers only actual transactions of dwellings, and is published quarterly, roughly three months after the quarter ends.

That difference is the reason listing data is valuable and the reason it needs careful labelling. Asking prices move before completed sales do, because a sale completes weeks or months after the price was agreed. The gap between asking and achieved price also widens and narrows with the cycle, so a model that treats one as the other will be wrong at exactly the turning points it is meant to catch.

Researchers who work with portal data are explicit about this. The documentation of RWI-GEO-RED, a research dataset built from adverts on Germany's largest property portal, states that the advertised price is an offering price, is not binding, and that the data do not comprise transaction prices.

Measuring days on market without fooling yourself

Days on market is not printed reliably on the page; you derive it. The honest definition is the span between the first and last run on which you saw a listing, at the cadence you ran. That makes the metric a function of your collection, and three things break it:

  • Missed runs. A gap shortens or splits a spell. Rate-limited runs that skip a day are the usual cause, which is why the sweep should be spread across a pool rather than sent from one address. See handling 429 rate limits.
  • Relisting. A listing taken down and re-posted gets a new ID and a fresh 'listed on' date. Unless you match it to the old one on features, it looks like a new property with zero days on market.
  • Paused adverts. Owners mark a property inactive while a sale is pending and reactivate it if the buyer withdraws. In the data that is a spell, a gap and another spell. The RWI-GEO-RED documentation lists this, template reuse and small edits to the advert as causes of duplicate spells.

The practical rule: decide on a merge window (for example, a listing that reappears within 30 days with matching features is the same spell), apply it consistently, and publish it alongside the metric.

Which plan for which part of a listing crawl

Not every request in a listing pipeline needs the same exit. Split the crawl by request type and price each part separately:

RequestTypical weightExitWhy
Search result pages80–150 KB, images blockedARC Basic, city-targetedThe bulk of the daily volume; light HTML; needs the city, not a consumer IP
Listing detail pages120–250 KB, images blockedARC Basic, fall back to ARC FastOnly for new or changed listings; move a portal up only when it fails
Gallery images1–5 MB per listingSame exit, once per listingFetch at first sighting and only for listings you keep
Paginated search that carries stateper page as aboveARC Fast, sticky 10 minKeeps one partition on one IP; see rotating vs sticky sessions

City targeting works on ARC Basic, ARC Fast and ARC Ultra from a published list of metros; country targeting covers 185+ countries. Check what a portal actually serves from a given exit before you trust a partition: how to test a proxy's location.

When to license instead of collecting

For some markets, portal data is available under licence, either commercially or, for academic work, through research data centres such as the FDZ Ruhr's RWI-GEO-RED for Germany. If a licensed feed covers your geography and history, it will usually be cleaner than anything you collect, because it comes with the portal's own identifiers and status flags.

Collecting yourself makes sense where no feed exists, where you need several portals combined, where you need daily rather than monthly granularity, or where you need markets a licensed dataset does not cover. Many teams do both: a licensed history for the backfill and their own daily collection for the recent edge.

Whatever you collect, keep it to public, logged-out pages, respect each portal's terms and robots.txt, and pace requests so a crawl does not burden the site. The same practices apply as on our parent page for real estate and job data.

Works with anything that takes a proxy URL

  • Scrapy
  • Python requests / httpx
  • Crawlee
  • Playwright
  • Docker

FAQ

Questions

Something not covered? Write to contact@arcproxies.io.

Is scraped listing data the same as house price data?

No. Listings show asking prices, which are what sellers hope to get. Official house price indices, such as Eurostat's, measure completed transactions and are published quarterly with a lag. Asking prices tend to move first, but they are a different series and should be labelled as one.

How do I calculate days on market from scraped listings?

Record the first and last date your crawler saw each listing, at a fixed cadence, and take the difference. Merge listings that reappear with matching features within a set window, so that relisting or a paused advert does not split one property into two short spells.

Which cities can I target for property data?

On ARC Basic, ARC Fast and ARC Ultra you can target cities from a published list in twelve countries, including Berlin, Munich, Hamburg, Frankfurt, Cologne, London, Manchester, Birmingham, Leeds, Paris, Lyon, Milan, Warsaw, Amsterdam, New York, Chicago, Toronto and Sydney. Country targeting covers 185+ countries. ARC Mobile has no city targeting.

How much bandwidth does a daily listing crawl use?

With images blocked, a result page is typically around 100 KB and a detail page a little more. A panel of 12 cities with 400 result pages and 600 changed listings per city per day comes to roughly 56 GB a month. Fetching galleries on every refresh is what makes the figure explode.

Do I need residential proxies for property portals?

Often not. Many listing pages are plain HTML served the same way to any address, and ARC Basic covers them with city targeting at our lowest price per GB. Move a specific portal to ARC Fast when you see failures on it that you cannot otherwise explain.

Can I store the agent's name and phone number?

Contact details of an identifiable person are personal data under the GDPR, which brings obligations such as a lawful basis, a retention limit and informing the people concerned. Most price and supply models do not need them, so the simplest approach is not to store them. Check specifics with counsel.

Sources & review

Last reviewed by the ARC Proxies team. Prices are read from the live catalog at build time.

  1. [1]Eurostat: Housing price statistics – house price index
  2. [2]RWI – Leibniz Institute for Economic Research, FDZ Ruhr: FDZ Data Description: Real-Estate Data for Germany (RWI-GEO-RED V14)

Get started

Buy a gigabyte and try it on your own targets.

No subscription, no sales call. Code WELCOME15 takes 15% off your first purchase, and whatever you do not use is still there next quarter.