Fotocasa Spain Property Scraper
Pricing
Pay per event
Fotocasa Spain Property Scraper
Scrape structured property listings from Fotocasa.es, Spain's #1 real-estate portal — price, rooms, bathrooms, surface m², full address, coordinates, features, description, and photo URLs. Per-listing fault isolation keeps one bad record from failing the run. Export to JSON or CSV.
Pricing
Pay per event
Rating
0.0
(0)
Developer
DevilScrapes
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
20 hours ago
Last modified
Categories
Share
🎯 What this scrapes
Fotocasa.es is Spain's #1 property portal and the default place millions of buyers, renters, and investors search for homes. This Actor walks its search-results pages — paste a location/zone slug or a full search URL — and pulls every listing's price, building type, rooms, bathrooms, surface area, full address (province/city/district/neighbourhood/zip), coordinates, dynamic features, up to ~18 photo URLs, and the full listing description, straight from the page's own embedded JSON payload. Every row is validated against a strict Pydantic schema before it ships, and one malformed listing never takes down the rest of the run. Built on curl-cffi with browser TLS impersonation and Spain-pinned residential proxy exits.
🔥 What we handle for you
- 🛡️ Browser fingerprint rotation —
curl-cffiimpersonates real Chrome / Firefox / Safari TLS handshakes so the target sees a browser, not Python. - 🌐 Residential proxy rotation via Apify Proxy — fresh session and exit IP on every block.
- 🔁 Retries with exponential backoff on
408 / 429 / 5xx— up to 5 attempts per page,Retry-Afterhonoured. - 🧱 Rate-limit-aware pacing — when the target pushes back, we slow down instead of getting banned.
- 🧊 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
- Market pricing analytics — track asking prices per m² by city, district, and neighbourhood across Spain.
- Investor / prop-tech feeds — pipe structured listings into a valuation or underwriting model.
- Buyer alerting — diff successive runs on a saved search to catch new listings and price drops.
- Agency benchmarking — aggregate
agency_aliasandagency_typeto map professional vs. private supply. - Geo/map integrations —
latitude/longitudeplug straight into mapping and clustering 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 |
|---|---|---|---|---|
searchUrl | string | no | None | Full Fotocasa.es search-results URL pasted from your browser (must start with https://www.fotocasa.es/).… |
locationSlug | string | no | None | Fotocasa location segment, e.g. madrid-capital, barcelona-capital. Copy it from a Fotocasa… |
zoneSlug | string | no | 'todas-las-zonas' | Fotocasa zone segment within the location, e.g. todas-las-zonas for all zones, or a specific district slug. |
transactionType | string | no | 'buy' | buy maps to Fotocasa's comprar section, rent to alquiler (rent-URL… |
propertyTypeId | integer | no | None | Raw Fotocasa propertyTypeId filter, copied from a filtered search URL. Leave empty for all property types. |
minPrice | integer | no | None | Exclude listings priced below this amount, in euros. Applied after fetching (client-side). |
maxPrice | integer | no | None | Exclude listings priced above this amount, in euros. Applied after fetching (client-side). |
maxPages | integer | no | 10 | Stop paginating after this many Fotocasa result pages (each page carries roughly 30 listings, but page size is read… |
maxResults | integer | no | 300 | Stop once this many validated listings have been pushed to the dataset, even mid-page. |
proxyConfiguration | object | no | {'useApifyProxy': True, 'apifyProxyGroups': ['RESIDENTIAL']} | Apify Proxy — RESIDENTIAL group, pinned to Spain (ES). A wrong-country exit risks wrong currency rendering or being… |
Example input
{"locationSlug": "madrid-capital","zoneSlug": "todas-las-zonas","transactionType": "buy","maxPages": 1,"maxResults": 3,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
📤 Output
Every row is one dataset item.
| Field | Type | Notes |
|---|---|---|
listing_id | integer | Fotocasa internal numeric listing ID. |
ad_id | string | Fotocasa advert UUID (realEstateAdId). |
detail_url | string | Absolute URL to the listing's detail page on fotocasa.es. |
price_display | string | Human-formatted price as shown on the site, e.g. '1.850.000 €'. |
price_eur | integer | Asking price as an integer in euros. |
reduced_price_eur | ['integer', 'null'] | Reduced/discounted price in euros, when the listing advertises one. |
building_type | string | Building type, e.g. Flat, House. |
building_subtype | ['string', 'null'] | Building subtype, e.g. SemidetachedHouse. |
transaction_type_id | integer | Fotocasa internal transaction type ID (sale vs rent). |
rooms | ['integer', 'null'] | Number of bedrooms. |
bathrooms | ['integer', 'null'] | Number of bathrooms. |
surface_m2 | ['integer', 'null'] | Surface area in square metres. |
address_country | ['string', 'null'] | Country name, e.g. España. |
address_province | ['string', 'null'] | Spanish province. |
address_city | ['string', 'null'] | City. |
address_district | ['string', 'null'] | District within the city. |
address_neighborhood | ['string', 'null'] | Neighbourhood within the district. |
address_zip_code | ['string', 'null'] | Postal code. |
latitude | ['number', 'null'] | Listing latitude. |
longitude | ['number', 'null'] | Listing longitude. |
dynamic_features | array | List of Fotocasa feature flags, e.g. IS_EXTERIOR, IS_LUXURY. |
image_urls | array | List of listing photo URLs (up to ~18). |
description | string | Full listing description text (Spanish). |
is_new | boolean | Whether the listing was recently published. |
is_new_construction | boolean | Whether the property is new-build construction. |
is_opportunity | boolean | Whether Fotocasa flags this listing as a price opportunity. |
listed_diff | ['integer', 'null'] | Numeric age of the listing in listed_unit units. |
listed_unit | ['string', 'null'] | Unit for listed_diff, e.g. DAYS. |
listed_timestamp_ms | ['integer', 'null'] | Original listing timestamp, epoch milliseconds. |
agency_alias | ['string', 'null'] | Listing agency or seller display name. |
agency_type | ['string', 'null'] | professional or private. |
phone | ['string', 'null'] | Contact phone number, when published. |
location_label | ['string', 'null'] | Short human-readable location label, e.g. Aravaca. |
Example output
{"listing_id": 190914947,"ad_id": "ad017f94-6c23-4057-b2e6-2219e71b8cbb","detail_url": "https://www.fotocasa.es/es/comprar/vivienda/madrid-capital/aire-acondicionado-terraza-zona-comunitaria-piscina/190914947/d","price_display": "1.850.000 €","price_eur": 1850000,"reduced_price_eur": null,"building_type": "Flat","building_subtype": "SemidetachedHouse","transaction_type_id": 1,"rooms": 8,"bathrooms": 6,"surface_m2": 485,"address_country": "España","address_province": "Madrid","address_city": "Madrid","address_district": "Moncloa - Aravaca","address_neighborhood": "Aravaca","address_zip_code": "28023","latitude": 40.45956755387569,"longitude": -3.7834746837336075,"dynamic_features": ["IS_EXTERIOR","IS_LUXURY","IS_VILLA"],"image_urls": ["https://static.fotocasa.es/images/ads/6674a2a0-e114-4f53-a1f8-220abe4e08d7?rule=original"],"description": "Gran Chalet adosado en el centro de Aravaca...","is_new": false,"is_new_construction": false,"is_opportunity": false,"listed_diff": 1,"listed_unit": "DAYS","listed_timestamp_ms": 1790161388100,"agency_alias": "FISOL","agency_type": "professional","phone": "+34627537863","location_label": "Aravaca"}
💰 Pricing
Pay-Per-Event — you pay only when these events fire:
| Event | USD | What it is |
|---|---|---|
actor-start | $0.20 | One-off warm-up charge per run |
result-scraped | $0.004 | Per unique dataset item |
Example: 1 000 results at the rates above ≈ $4.20. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.
🚧 Limitations
- Detail-page-only fields (if any exist beyond what the search page embeds) are out of scope for v1 — see the FAQ.
- Rent (alquiler) URLs are supported best-effort; sale (comprar) is the wire-confirmed path.
- Price filters are applied after fetching each page, not as a Fotocasa URL parameter, so a narrow price band still costs the request for out-of-range listings on that page.
propertyTypeIdis passed through as Fotocasa's raw internal ID — there is no confirmed ID-to-label table yet.
❓ FAQ
Do I need a Fotocasa account or API key?
No. Fotocasa publishes no public API for this data. This Actor reads the same public search-results pages a browser loads and parses the structured JSON already embedded in them.
How do I scrape a specific search?
Apply your filters on fotocasa.es, copy the resulting URL from your browser, and paste it into searchUrl. Or set locationSlug/zoneSlug/transactionType directly if you already know the slugs.
Does this fetch each listing's detail page?
Not in v1. The search-results page already embeds price, address, coordinates, features, up to ~18 photo URLs, and the full description per listing — a per-listing detail fetch would multiply request volume for no confirmed extra field.
Is rent (alquiler) supported?
Yes, best-effort — set transactionType to rent. The URL shape mirrors the sale (comprar) flow but wasn't independently wire-confirmed at build time; the run fails loud rather than silently mis-parsing if Fotocasa's rent pages differ.
Why did my run return zero rows?
Either your filters matched nothing on Fotocasa (the run still succeeds, with a status message naming the search), or the price bounds you set excluded every result on the page — try widening minPrice/maxPrice.
💬 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.