Idealista Property Scraper
Pricing
from $5.00 / 1,000 result row emitteds
Idealista Property Scraper
Scrape normalized property listings from idealista.com (Spain), idealista.it (Italy), and idealista.pt (Portugal) in one run — unified EUR/m² schema, sale or rent, plus agency and lister contact enrichment neither incumbent ships together.
Pricing
from $5.00 / 1,000 result row emitteds
Rating
0.0
(0)
Developer
DevilScrapes
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
🎯 What this scrapes
Idealista is the shared real-estate platform behind idealista.com (Spain), idealista.it (Italy), and idealista.pt (Portugal) — the #1 property portal in every one of those markets. Cross-border investors and PropTech teams today stitch together three separate scrapes with three different schemas just to compare a Madrid flat to a Lisbon apartment. This Actor wraps all three TLDs into a single run and a single normalized schema — EUR pricing, m² area, a shared property-type taxonomy, and EU energy ratings — so a country field is the only thing that changes between rows. On top, every row already carries the agency/lister name straight from the search page, and an optional detail-page pass adds bathrooms, energy rating, coordinates, and the agency's own reference code — fields most Idealista scrapers skip entirely.
🔥 What we handle for you
- 🛡️ A real browser engine, not a bare HTTP client — we drive an anti-detection browser through a same-context warm-up sequence so the target sees genuine browser traffic, never a bot signature.
- 🌐 Residential proxy rotation via Apify Proxy, pinned per country — a fresh exit IP on every retry, never the same flagged exit twice.
- 🧱 A geo-splash guard on every response — a mismatched exit can return a different country's listings at a clean 200; we catch and reject that before it ever reaches your dataset.
- 🔁 Automatic retries with a freshly rotated proxy session on any block signal.
- 🧊 Clean, typed dataset rows — Pydantic-validated, ISO-8601 timestamps, stable IDs, JSON / CSV / Excel export straight from the Apify Console.
- 💰 Pay-Per-Event pricing — you only pay for results that hit your dataset. No data, no charge.
💡 Use cases
- Cross-border deal screening — compare sale listings in Madrid, Rome, and Lisbon side by side without normalizing three different scrapers' output yourself.
- Rental market benchmarking — track price-per-m² across ES/IT/PT rental markets in one dataset.
- Agency/lister lead lists — collect agency names and agent reference codes behind active listings (enable enrichAgencyContact for the reference code).
- Relocation and expat services — build a unified property feed spanning three Southern European markets for client-facing search tools.
⚙️ How to use it
- Click Try for free at the top of the page.
- Fill in the input form — most fields have sensible defaults.
- Click Start. Output streams into the run's dataset.
- Export from Storage → Dataset as JSON, CSV, or Excel — or fetch via the API.
📥 Input
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
countries | array | no | ['ES'] | Countries to run (ES/IT/PT). Each country runs independently — one blocked country doesn't stop the others. |
searchUrls | array | no | '—' | Direct idealista.com / idealista.it / idealista.pt search URLs to scrape as-is. Country is inferred from the TLD. Up to… |
location | string | no | 'madrid-madrid' | idealista's own two-part search slug — municipality-province, e.g. 'madrid-madrid' (most municipalities do NOT share… |
operation | string | no | 'sale' | Sale vs. rent. Country-agnostic — mapped to the right per-TLD slug internally. |
propertyType | string | no | 'homes' | Shared property-type taxonomy across all three TLDs. |
maxResultsPerCountry | integer | no | 100 | Cap on rows per country; each country's cap is independent. |
enrichAgencyContact | boolean | no | False | Fetch each listing's detail page and add bathrooms, a richer floor label, EU energy rating letter (when the listing's… |
proxyCountryMap | object | no | {'ES': 'ES', 'IT': 'IT', 'PT': 'PT'} | Override the proxy exit country per target country. Idealista hard-localizes by IP — a mismatched exit silently returns… |
proxyConfiguration | object | no | {'useApifyProxy': True, 'apifyProxyGroups': ['RESIDENTIAL']} | Mandatory-by-default RESIDENTIAL — country_code is resolved per-request from Proxy country map. |
Example input
{"countries": ["ES"],"location": "madrid-madrid","operation": "sale","propertyType": "homes","maxResultsPerCountry": 3,"enrichAgencyContact": false,"proxyCountryMap": {"ES": "ES","IT": "IT","PT": "PT"},"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
📤 Output
Every row is one dataset item.
| Field | Type | Notes |
|---|---|---|
listing_id | string | Idealista ID, per-TLD. |
url | string | Canonical detail URL. |
country | string | Normalization key — 'ES', 'IT', or 'PT'. |
operation | string | 'sale' or 'rent', as requested. |
property_type | string | Shared taxonomy value. |
title | string | Listing headline. |
price | ['number', 'null'] | Asking price. |
currency | string | Always 'EUR'. |
price_per_sqm | ['number', 'null'] | Published or computed. |
area_sqm | ['number', 'null'] | Normalized to m². |
rooms | ['integer', 'null'] | Room count. From the search page on every run. |
bathrooms | ['integer', 'null'] | Bathroom count. Only available with enrichAgencyContact on. |
floor | ['string', 'null'] | Raw label, not normalized (per-market variance). |
energy_rating | ['string', 'null'] | EU EPC letter A–G. Only available with enrichAgencyContact on, and only when the listing's own detail page renders a… |
location_address | ['string', 'null'] | Street address. Not currently resolvable from idealista's own page markup — always null. |
location_neighborhood | ['string', 'null'] | Neighborhood, when public. |
location_city | ['string', 'null'] | City, when resolvable. null (never a placeholder string) when idealista's own markup didn't expose it for this listing. |
location_province | ['string', 'null'] | Province/region. Not currently resolvable from idealista's own page markup — always null. |
latitude | ['number', 'null'] | WGS-84 map pin latitude. Only available with enrichAgencyContact on. |
longitude | ['number', 'null'] | WGS-84 map pin longitude. Only available with enrichAgencyContact on. |
agency_name | ['string', 'null'] | Agency/lister name, from the search page on every run. |
agency_phone | ['string', 'null'] | Not exposed anywhere in idealista's page markup — revealing it requires an authenticated AJAX call this Actor does not… |
listing_owner_type | ['string', 'null'] | 'particular' or 'professional'. Not reliably derivable from idealista's current page markup — always null rather than… |
agent_reference_id | ['string', 'null'] | Agency's own reference code. Only available with enrichAgencyContact on. |
listed_date | ['string', 'null'] | ISO YYYY-MM-DD. Not currently resolvable from idealista's own page markup — always null. |
updated_date | ['string', 'null'] | ISO YYYY-MM-DD. Not currently resolvable from idealista's own page markup — always null. |
description_snippet | ['string', 'null'] | First ~300 chars. From the search page on every run. |
image_urls | array | Gallery photos. 1-3 from the search page on every run; the full gallery with enrichAgencyContact on. |
scraped_at | string | ISO-8601 UTC row-creation timestamp. |
Example output
{"listing_id": "111433413","url": "https://www.idealista.com/inmueble/111433413/","country": "ES","operation": "sale","property_type": "homes","title": "Piso en Calle del Barco, Malasaña-Universidad, Madrid","price": 750000.0,"currency": "EUR","price_per_sqm": 7352.94,"area_sqm": 102.0,"rooms": 2,"bathrooms": 2,"floor": "1ª planta interior con ascensor","energy_rating": "D","location_address": null,"location_neighborhood": "Malasaña-Universidad","location_city": "Madrid","location_province": null,"latitude": 40.4232645,"longitude": -3.7037263,"agency_name": "Gilmar Centro","agency_phone": null,"listing_owner_type": null,"agent_reference_id": "AG222680","listed_date": null,"updated_date": null,"description_snippet": "GILMAR Consulting Inmobiliario pone a su disposición este sofisticado y acogedor piso de 102 m², ubicado en el corazón del emblemático barrio de Chueca, en pleno centro de Madrid.","image_urls": ["https://img4.idealista.com/blur/591_420_mq/0/id.pro.es.image.master/6c/dd/2c/1441278735.jpg","https://img4.idealista.com/blur/189_120_mq/0/id.pro.es.image.master/46/b8/f4/1437239139.jpg"],"scraped_at": "2026-09-22T09:30:26+00:00"}
💰 Pricing
Pay-Per-Event — you pay only when these events fire:
| Event | USD | What it is |
|---|---|---|
actor-start | $0.2 | One-off warm-up charge per run |
result-row | $0.005 | Per unique dataset item |
Example: 1 000 results at the rates above ≈ $5.20. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.
🚧 Limitations
v1 covers Spain, Italy, and Portugal only — Idealista doesn't operate in other markets. New-development/promoter microsites, the mortgage calculator, area-guide pages, and saved-search/alert features behind an agency login are out of scope. agency_phone and listing_owner_type (professional vs. private) are not exposed anywhere in idealista's own page markup on any of the three sites and always ship null — revealing a phone number requires an authenticated request this Actor does not make. location_address (a clean street), location_province, listed_date, and updated_date are likewise not resolvable from the page and always ship null. bathrooms, energy_rating, latitude/longitude, and agent_reference_id only populate with enrichAgencyContact on — see Pricing for the cost trade-off. floor stays a raw label — market-specific floor conventions aren't normalized across countries.
❓ FAQ
Do I need an Idealista account or API key?
No. This Actor scrapes Idealista's own publicly listed property data across all three TLDs — no login, no API key.
Why does this default to a residential proxy pinned per country?
Idealista localizes by IP — a mismatched exit can silently return a different country's listings. We default every request to a RESIDENTIAL proxy pinned to the target country so ES/IT/PT runs never cross-contaminate.
Can I mix countries in one run?
Yes — set countries to any combination of ES/IT/PT (or supply direct searchUrls), and each country is fetched independently; one blocked country never stops the others.
Does agency_phone ever populate?
No — idealista doesn't expose a phone number in any page's static markup on any of the three sites; revealing one requires an authenticated request this Actor doesn't make. agency_name and agent_reference_id (with enrichAgencyContact on) are the reliable contact fields instead.
💬 Your feedback
Spotted a bug, hit a weird edge case, or need a new field? Open an issue on the Actor's Issues tab on Apify Console — we ship fixes weekly and we read every report.