# Fotocasa Scraper - Spain Property Listings, Rent & Sale (`amitaviv/fotocasa-scraper`) Actor

Scrape Fotocasa.es (Spain) homes and commercial property for sale and rent from a search URL or a place name with filters. One record per listing: price, €/m², price drops, m², rooms, floor, address, coordinates, photos, agency. New-listings monitoring. $0.70 per 1,000 listings on Bronze.

- **URL**: https://apify.com/amitaviv/fotocasa-scraper.md
- **Developed by:** [Amit Aviv](https://apify.com/amitaviv) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Fotocasa Scraper (fotocasa.es, Spain)

Returns property listings from Fotocasa (fotocasa.es), one of Spain's main property portals, for sale and for rent. It works from search URLs you copy out of the browser, or from a place name and a few filters. It uses plain HTTP and no browser, so a run of a few hundred listings takes seconds. Apart from the start fee, you pay only for delivered listings. A run that delivers nothing because of an error ends as Failed, never as a green empty run.

**Not for re-publication.** Fotocasa's listings are protected by database rights. This actor is for market analysis, research and monitoring. Re-publishing listings, or building a competing listings site from the output, is not permitted. See "Legal and responsible use".

### What you get

- Price, price period (per month for rentals and shared rooms), price per m², and price drops (previous price, the drop in EUR and in percent) where Fotocasa shows one
- Size in m², bedrooms, bathrooms, floor as text (ground floor, 1 … 15, above 15th, penthouse …) and as a number, condition, property type and subtype (flat, penthouse, duplex, house, studio …)
- Air conditioning, heating, lift, terrace, balcony, garden, swimming pool and parking flags
- Address as published (street when the advertiser shows it; for private sellers without house number or door), neighbourhood, district, town, province, region, postcode, coordinates
- Full description text, all photo URLs, the virtual-tour and video links when there are any
- Publication date (exact timestamp), "new" and "featured" flags, new-development name
- Private/professional flag. For professional advertisers (agencies, developers, banks): the name, business phone, Fotocasa agency page and logo
- One record per listing, as JSON, CSV or Excel, or through the API
- A run summary (`OUTPUT_SUMMARY` in the run's key-value store): what was requested, delivered and charged; per search, the place it was matched to, how many listings Fotocasa reports and how many price bands were read; which inputs failed or were ignored; why the run stopped

What it does not return:

- energy certificate, orientation, "furnished" and other extras that appear only on the listing's own page (`moreFeaturesOnSite` tells you how many such extras the listing has)
- private sellers' name, phone and contact details, exact door address and exact coordinates (see "Private sellers")
- anything behind a login
- listings outside Spain

### How to use

1. Open fotocasa.es and make the search you want: town, district or neighbourhood, with any filters. Then copy the URL from the browser.
2. Paste it into **Search URLs**. Several entries are fine. Or leave it empty and type a place into **Place** and use the filter fields.
3. Set **Max listings per search** and press Start.
4. Download the results from the Output tab, or read them from the API.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `startUrls` | list of text | none | Fotocasa search URLs (`…/<transaction>/<type>/<place>/<zone>/l`), from the Spanish, Catalan or English site. All types work: homes, garages, land, commercial premises, offices, storage rooms and buildings. So do all transactions: sale, rent, share, holiday rent, business transfer and rent-to-own. The URL's filters (price, bedrooms, bathrooms, size, subtypes, condition, features, publication date …) and its sort order are applied as they are. A page number in the URL is ignored: reading starts at page 1. Duplicates are ignored. URLs from other sites, listing pages and the forms below are ignored and listed in the summary with the reason. |
| `location` | text | none | Instead of a URL: a town, district, neighbourhood or province as you would type it in Fotocasa's search box. Separate several with `;`. The site's first suggestion is used, and the summary shows which place that was, e.g. `Málaga (province)`. |
| `transaction`, `propertyType` | choice | sale, home | Used with `location`. |
| `priceMin`, `priceMax`, `roomsMin`, `roomsMax`, `bathroomsMin`, `areaMin`, `areaMax`, `sortBy` | number / choice | none | Used with `location`. `priceMax` includes the value itself. A value that is not a number, or a minimum above its maximum, fails the run before any request. |
| `maxItemsPerQuery` | number | 100 | Stop after this many listings for each URL or place. |
| `splitLargeSearches` | yes/no | yes | Only matters when the maximum is above 9,900. See "Large searches". |
| `includePrivateSellerData` | yes/no | no | See "Private sellers". |
| `onlyNew`, `monitorKey` | yes/no, text | no, empty | Monitoring. See "Monitoring: only new listings". |
| `proxyConfiguration` | proxy | off | Not needed in our tests. Turn on Apify Proxy only if a run reports blocking. |

URL forms that are ignored, with the reason given in the summary: searches around a street or point of interest (`/area/…`, `/poi/…`); "all property types" (`/inmuebles/`); extra path words that are not a property subtype (e.g. `…/todas-las-zonas/con-piscina/l`); map views whose area was drawn or moved (`…/m?zoom=…`). In those cases, paste the list view of the search (`…/l`) without that part and use the site's filters instead, which are kept.

Two forms are read with a warning in the run log and summary: a plain map view (`…/m` with only the list's filters) is read as the list of the same place, and a `combinedLocationIds` in the URL that is not a valid Fotocasa place id is dropped, so the place comes from the URL path (Fotocasa itself would answer such an id with all of Spain).

```json
{
  "startUrls": ["https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/chamberi/l"],
  "location": "Málaga",
  "transaction": "rent",
  "maxItemsPerQuery": 300
}
```

This input was run as `PKMqVpsTb4BVpCddl` on 2026-10-14 (build 0.1.6). It delivered 600 listings in 6.8 seconds (300 per search), with 600 unique ids, and charged 600. "Málaga" matched the province, as the run summary shows (`Málaga (province)`).

### Example result

Taken from run `PKMqVpsTb4BVpCddl` (2026-10-14). The image list is shortened to 2 of 12 URLs; nothing else is edited.

```json
{
  "id": "190014262",
  "url": "https://www.fotocasa.es/es/alquiler/vivienda/madrid-capital/aire-acondicionado-calefaccion-ascensor/190014262/d",
  "transaction": "rent",
  "propertyType": "home",
  "propertySubtype": "flat",
  "propertySubtypeId": 1,
  "price": 1300,
  "currency": "EUR",
  "pricePeriod": "month",
  "priceOnRequest": false,
  "pricePerM2": 26,
  "previousPrice": 1900,
  "priceDrop": 600,
  "priceDropPercent": 31.6,
  "sizeM2": 50,
  "rooms": 1,
  "bathrooms": 1,
  "floor": "1",
  "floorNumber": 1,
  "condition": "good",
  "hasAirConditioning": true,
  "hasHeating": true,
  "hasElevator": true,
  "hasTerrace": false,
  "hasBalcony": true,
  "hasGarden": false,
  "hasSwimmingPool": false,
  "hasParking": false,
  "features": ["air_conditioner", "heater", "elevator", "balcony"],
  "moreFeaturesOnSite": 4,
  "address": "Calle de Magallanes, Arapiles",
  "neighbourhood": "Arapiles",
  "district": "Chamberí",
  "municipality": "Madrid Capital",
  "province": "Madrid",
  "region": "Madrid",
  "zipCode": "28015",
  "latitude": 40.434998,
  "longitude": -3.7056482,
  "coordinatesAccuracy": 0,
  "description": "PROPIETARIOS TRANQUILOS pone en ALQUILER  piso en Madrid  capital.\n\n\nInmueble sito en 1ª  planta con ascensor que se distribuye en 1 habitación, salón  con cocina americana con  acceso a balcón y 1 baño.\nLa vivienda cuenta con  cocina totalmente amueblada y equipada , acondicionado, armario empotrado, ventanas de climalit, agua caliente por termo eléctrico \n\n¡!LLÁMANOS PARA MÁS INFORMACIÓN E IR A VERLO!!",
  "imageUrl": "https://static.fotocasa.es/images/ads/229f31ac-fed5-4193-a79c-c594d142ea2f?rule=original",
  "imageCount": 12,
  "images": [
    "https://static.fotocasa.es/images/ads/229f31ac-fed5-4193-a79c-c594d142ea2f?rule=original",
    "https://static.fotocasa.es/images/ads/eb50a0c6-9ec5-47da-a1c3-c2511728b096?rule=original"
  ],
  "virtualTourUrl": null,
  "videoUrl": null,
  "advertiserType": "professional",
  "advertiserName": "Propietarios Tranquilos",
  "advertiserPhone": "+34911124579",
  "advertiserId": "9202762649652",
  "advertiserUrl": "https://www.fotocasa.es/inmobiliarias/propietarios-tranquilos-9202762649652",
  "advertiserLogo": "https://static.fotocasa.es/images/client/d7353a56-5b80-4b05-b13b-edfd28ea852e/20260420191227?rule=original",
  "privateDataRedacted": false,
  "isNewDevelopment": false,
  "developmentName": null,
  "isFeatured": true,
  "isNew": false,
  "publishedAt": "2026-06-18T14:53:13.600Z",
  "externalContactUrl": null,
  "search": "https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/chamberi/l",
  "scrapedAt": "2026-10-14T16:43:29.047Z"
}
```

| Field | Meaning |
|---|---|
| `id` | Fotocasa's own listing id; stable between runs. |
| `price`, `pricePeriod` | Price in EUR; `month` for long-term rentals and for shared rooms (`share`), `null` for sales. For shared rooms Fotocasa's search data often states no period, although the site shows the price per month (e.g. `550€/mes`), so those are delivered as `month`. A rental whose period Fotocasa does not state (rare: 2 of 100 Madrid rentals) has `null`. Price `null` with `priceOnRequest: true` when the advertiser shows no price. |
| `pricePerM2` | `price / sizeM2`, rounded to the cent (e.g. 1,600 / 95 m² = 16.84). For rentals it is monthly rent per m². `null` when the price or the size is unknown. |
| `sizeM2` | Size in m² as the advertiser gives it. Fotocasa stores 1 m² as a placeholder on many listings without a size (about 10% of Valencia rentals, mostly Spotahome and Uniplaces); such placeholders are delivered as `null`: any size of 1 m² or less, and homes under 10 m². Garages and storage rooms from 2 m² are kept. |
| `priceDrop`, `previousPrice`, `priceDropPercent` | Fotocasa shows a "price went down by X €" badge on some listings. `priceDrop` is X, `previousPrice` is price + X, and `priceDropPercent` is X / previousPrice. All three are `null` when there is no badge. |
| `rooms` | Bedrooms (habitaciones). |
| `floor` | `ground floor`, `mezzanine`, `principal`, `1` … `15`, `above 15th`, `penthouse`, `basement`, `sub-basement`, `other`, or `null` when not given. |
| `floorNumber` | The same as a number: `0` ground floor, `-1` basement, `-2` sub-basement, `1` … `15`, `16` for "above the 15th". `null` for mezzanine, principal, penthouse, other and unknown. |
| `has…` flags, `features` | `true` when the listing states it. `false` means not stated, which is not the same as "no". |
| `moreFeaturesOnSite` | How many further extras (furnished, appliances, storage room …) the listing's own page shows; they are not in the search data. |
| `address` | As the advertiser published it: sometimes a street, often only a neighbourhood. For private sellers (by default) without house number, floor or door. |
| `latitude`, `longitude` | As Fotocasa gives them. For private sellers (by default) rounded to 3 decimals, about 100 m. |
| `coordinatesAccuracy` | Fotocasa's own accuracy code for the coordinates, passed through unchanged (0 on 184 of 200 listings in a sample; we did not find what the other values mean). |
| `publishedAt` | Date and time Fotocasa gives the listing (publication or last renewal), in UTC. Fotocasa writes Madrid local time; the actor converts it, so it is two hours earlier than the site's raw value in summer and one hour earlier in winter. |
| `advertiserType` | `professional` (agency, developer, bank), `private` (a private person), or `unknown` when Fotocasa gives no advertiser type or one it did not use before (none seen so far); `unknown` is treated like `private`. |
| `privateDataRedacted` | `true` when this is a private (or `unknown`) seller's listing and their data were removed or coarsened. |
| `search` | The search the listing came from: the URL as read (page number, sort and tracking parameters dropped), or for a typed place the place and filters, e.g. `Chamberí: home for rent, priceMin 1000, priceMax 2000`. |

Fields that Fotocasa does not provide for a listing are `null` (or `false` for the flags), never missing.

### Pricing

Pay per event. You pay for what is delivered, not for time or memory.

| Event | Free | Bronze | Silver | Gold and above |
|---|---|---|---|---|
| Actor start (once per run at the default 256 MB) | $0.0036 | $0.003 | $0.0027 | $0.0024 |
| Listing delivered | $0.00084 | $0.0007 | $0.00063 | $0.00056 |

Worked examples on Bronze:

- One search with 200 listings costs $0.003 + 200 × $0.0007 = **$0.143**. That is $0.70 per 1,000 listings, plus the start fee.
- A scheduled monitoring run that finds 3 new listings costs $0.003 + 3 × $0.0007 = $0.0051. A run that finds nothing new costs $0.003.
- A whole city (Madrid, homes for sale, 12,075 listings, run `274XfLMQ3vhlwj83D`) costs $0.003 + 12,075 × $0.0007 = $8.46.

How charging works:

- Listings that were not delivered are not charged. A listing found by two of your searches in one run is delivered and charged once.
- The maximum total charge you set for a run is respected. The actor stops before it would go over, keeps what was delivered, and says so in the status message (run `wBqbaacgaUz1JoP2b`: limit $0.0079, exactly 7 listings delivered and charged).
- The start fee is charged by the Apify platform on every run, including runs that fail on bad input. It is charged once per GB of memory, so keep the default 256 MB. If the platform restarts a run on another server, it charges the start fee again for the restart; listings are still charged once (run `lVV2kQOeerIOomKDJ`: restarted, 2,592 listings delivered and charged, start fee 2).

### Reliability and failure behaviour

| What happened | Run status | Status message (from real runs) |
|---|---|---|
| Listings delivered | Succeeded | `Scraped 150 listings from 1 query` (`UTDvdWfvXG7HN8nfM`) |
| Fotocasa has no listings for your search | Succeeded | `0 results: Fotocasa has no listings for this query` (`iFSKP9bcG1hHhvdAS`) |
| Some searches worked, some failed | Succeeded | `Scraped 120 listings from 2 queries — 1 of 2 queries failed ("…": …)` |
| Nothing delivered because of errors or blocking | **Failed** | `No listings delivered: Fotocasa blocked every attempt; try again later. No listings were charged (the start fee applies).` |
| A place Fotocasa does not know | **Failed** | `No listings delivered: Fotocasa does not know the place "ciudad-inexistente/todas-las-zonas" of this URL: …` (`Z8wmR6VkGSkbqNbTD`) |
| Input the actor cannot use | **Failed**, in about 1 second | `Nothing to do: the input has no "startUrls". Add at least one entry to "startUrls", e.g. …` (`8YycER8yyVuqTnjgV`) |
| Maximum charge reached | Succeeded | `Scraped 7 listings from 1 query — stopped at the maximum charge set for this run` (`wBqbaacgaUz1JoP2b`) |
| Maximum charge below one listing | **Failed**, before any request | `The maximum charge set for this run ($0.0033) is below the price of one listing …` (`E2ulq4tFLcU9jrilh`) |
| Run timeout close | Succeeded | `… — stopped just before the run timeout; raise the timeout to get the rest` |

- Requests are retried with increasing pauses on network errors, HTTP 403/429 blocks and 5xx errors: up to 4 retries without a proxy, 6 with one.
- Listings are saved page by page. A run that is aborted, times out or is resurrected keeps what was delivered, and never delivers or charges the same listing twice.
- In our 42 test runs on 2026-10-14, all 568 requests counted in the run summaries were answered on the first try (0 retries, 0 blocks), and all but one run used Apify's own addresses with no proxy. In every run, the listings charged equalled the listings delivered. The "blocked" path is covered by offline tests only; we never saw a block.

### Large searches

Fotocasa serves at most about 9,900 listings of one search. If you set **Max listings per search** above 9,900 (and keep **Split large searches** on), a larger search is cut into price bands that each fit. The bands are read one after the other, cheapest first. Each listing is delivered once.

Because the bands go cheapest first, a split search that stops early (on the maximum per search, the run's maximum charge or the timeout) holds only its cheaper part, not a sample of the whole search. The status message then says up to which price the result is complete. Run `IQB0nsLcqP5YH33aU` (Málaga province, homes for sale, 44,556 listings in 9 bands, maximum charge $0.213) delivered and charged 300 listings and ended with: `… is read in price bands, cheapest first, and stopped early: inside the cheapest band, so every listing delivered is priced 7,746 EUR to below 72,668 EUR; this is not a sample of the whole search; stopped at the maximum charge set for this run`.

Run `274XfLMQ3vhlwj83D` (Madrid capital, homes for sale) is an example. Fotocasa reported 12,175 listings, read in 2 price bands. The run delivered 12,075 listings, all with distinct ids, in 6 minutes, and charged 12,075.

The 99 listings without a price ("price on request") are the gap. They fall outside every price band, and Fotocasa sorts them last in every price order. So they cannot be reached in a split search, and the run summary and status message give their number. In searches below 9,900 listings they are delivered normally, with `priceOnRequest: true`.

With splitting off, or with a maximum of 9,900 or less, a larger search delivers the first 9,900 in the order you chose. The status message says how many more Fotocasa has.

### Monitoring: only new listings

1. Turn on **Only new listings** and save the input as a task.
2. Schedule the task, for example hourly or daily.
3. The first run delivers the newest listings, up to the maximum. Every later run delivers and charges only listings it has not delivered before for the same search. A run with nothing new ends as Succeeded with `0 new listings…`, and charges only the start fee.

Searches are read newest first, whatever order the URL asks for, and stop at the first page that holds a known listing. Tested live:

- Barcelona rentals: run `zpswJB1Dq4wZRfgp9` delivered 50 listings. Run `4FRQcvSewbRT1tUEd` shortly after delivered 0, charged 0 listings and read one page.
- All of Spain, homes for sale: run `Qhahek9AJOHpU2l60` delivered 100. Run `4Q58y8Wki3LrtKG9H`, 3 minutes later, delivered the 20 listings published in between, none of which the first run had delivered, and read one page.

The memory is a key-value store named `fotocasa-scraper-monitor` in your own account. It holds one record per search and monitor name, at most 10,000 ids each, with the oldest forgotten first. Delete the store or change **Monitor name** to start over. Set the maximum above the number of new listings you expect between two runs: later runs read only down to the first known listing, so new listings beyond the maximum are not delivered. To be notified, add an integration to the task (email, Slack or webhook) on "run succeeded".

A listing that the advertiser renews gets a new date and moves to the top. If an earlier run delivered it, it counts as known and is not delivered again.

### Private sellers

About 8% of the listings in our Madrid sample (994 of 12,075) were published by private persons. For these, by default:

- the advertiser's name, phone, Fotocasa id and page are `null`
- the address loses its house number, floor and door (`Carrer de Pere Aleixandre, 13, Quatre Carreres` becomes `Carrer de Pere Aleixandre, Quatre Carreres`), and the coordinates are rounded to 3 decimals, about 100 m
- phone numbers, e-mail addresses (also written as `name(at)mail.com` or `[arroba]`), web links and @handles are removed from the description
- `privateDataRedacted` is `true`

The same applies when Fotocasa gives no advertiser type, or one it did not use before (`advertiserType: "unknown"`): only listings Fotocasa marks as professional are delivered with contact data.

**Not removed:** first names or full names that a private seller writes inside the description itself (e.g. "preguntar por Paqui"). The actor cannot tell a name from other words reliably. Do not use them to contact anyone.

The listing itself (price, size, street, neighbourhood, photos) is delivered as for any other listing. In run `OYV5gzZA8MdXqkCpe` (Valencia rentals, 2,587 listings, 278 private), every private listing had name, phone, id, page and logo `null`, no address with a number, coordinates at 3 decimals, and no phone number or e-mail address left in the texts.

`includePrivateSellerData` delivers private sellers' data as published (name and phone if Fotocasa has them, the full address, exact coordinates and the untouched description). Switch it on only if you have a lawful basis under the GDPR to process it, for example their consent. In our samples, Fotocasa's search data carried no phone number for any private seller. About half carried a name: a first name, a full name, or sometimes an e-mail address used as a name (14 of 29 private sellers in run `KL7zoOCvT3MbgAcer`). All of this is removed by default. Agencies and developers are delivered in full either way.

### Using it together with our Idealista Scraper

Many Spanish listings appear on both portals, and many do not. For a fuller view of a Spanish market, run this actor and our [Idealista Scraper](https://apify.com/amitaviv/idealista-scraper) on the same area. Many columns have the same names and meaning in both: `price`, `pricePerM2`, `previousPrice`, `priceDropPercent`, `sizeM2`, `rooms`, `advertiserType`, `isNewDevelopment`, `images`, `url`. Idealista adds coordinates when its full details are switched on.

Before stacking the two in one typed table (pandas, BigQuery …), mind three columns:

- `floor` is text here (`"1"`, `"ground floor"`, `"penthouse"`) and a number in the Idealista Scraper. Use this actor's `floorNumber` instead: it follows Idealista's numbering (0 ground, -1 basement).
- `has…` flags (`hasElevator` and others) are `true` or `false` here, where `false` means "not stated". In the Idealista Scraper an unknown value is `null`.
- `id` values come from different sites and can collide; keep a source column.

### Limits

- At most about 9,900 listings per search without splitting; see "Large searches".
- Search data only. The extras that appear only on a listing's own page (energy certificate, orientation, furnished …) are not included.
- `has…` flags cover the eight features Fotocasa's search data carries; `false` means "not stated".
- Names a private seller writes inside the description are not removed (see "Private sellers").
- Speed: 300 listings per search in about 3 seconds (`PKMqVpsTb4BVpCddl`: 600 listings from 2 searches in 6.8 s). A read of 12,075 listings took 6 minutes (`274XfLMQ3vhlwj83D`): the first 1,000 came at about 140 a second, later pages at about 30 a second.
- The URL forms listed under "Input" are not supported. Map views with a drawn or moved area are refused; we did not have a real drawn-polygon URL to test, so a map URL whose area travels in a parameter we do not know of could still be read as the whole place (the run log then warns that a map URL was read as the list).
- Spain only (Fotocasa covers only Spain).

### FAQ

**Do I need a proxy?** No. In our tests every request from Apify's servers was answered without one. A run with Apify Proxy on also worked (`usNctG8b3hJHkdGwu`), but was slower.

**Why did I get fewer listings than the site shows?** Your maximum per search is lower than the site's count. Or the search is above 9,900 and splitting is off (the message says so). Or it is a split search, where listings without a price are left out (the message gives the number).

**Can I run it from code?** Yes: through the API, the Apify clients for JavaScript and Python, or Make, Zapier and n8n. The Output tab shows the URLs.

**What happens when Fotocasa changes?** Runs fail loudly instead of returning empty data. Report the run id under Issues.

### Legal and responsible use

This actor collects property listings that Fotocasa shows publicly without a login. It does not log in and does not use contact forms.

By default it does not return private persons' contact data. Where a listing comes from a private seller, the record says `advertiserType: "private"` and the seller's name, phone, id and page are `null`. Phone numbers, e-mail addresses, links and handles are also removed from their text.

Fotocasa's terms of use limit the use of its content to personal needs and forbid commercial exploitation and re-publication without permission. Listings are also protected by the database right of the EU and Spain. You are responsible for how you use the data. Respect those terms, copyright and database rights, and data-protection law (GDPR, LOPDGDD).

Do not use the output to contact private sellers. The Spanish data-protection authority fined an agency in 2025 for cold-contacting an owner with a phone number taken from a listing. If you are unsure, ask a lawyer before you build on the data.

### Changelog

- 2026-10-14 0.1.8: shared rooms (`share`) whose period Fotocasa's search data does not state get `pricePeriod: "month"`, as the site shows them; README wording on private sellers' names corrected.
- 2026-10-14 0.1.7: placeholder sizes (1 m², homes under 10 m²) become `null`, so `pricePerM2` is no longer wrong for them; `pricePerM2` rounded to the cent; new `floorNumber`; private sellers' address without house number and coordinates to about 100 m; unknown advertiser types treated as private; `searchUrl` renamed `search`; overview shows type and subtype; an invalid `combinedLocationIds` in a URL is dropped instead of searching all of Spain; map URLs with a drawn area refused; province names read "Málaga (province)"; a split search that stops early says which prices it covers.
- 2026-10-14 0.1: first version. Search URLs and filters, price-band splitting for large searches, new-listings monitoring, private sellers' data removed by default.

### Related actors

- [Idealista Scraper](https://apify.com/amitaviv/idealista-scraper): Spain's other large property portal (also Portugal and Italy); many listings are on only one of the two, and the main columns have the same names (see "Using it together with our Idealista Scraper").
- [Idealista Rental Yield Finder](https://apify.com/amitaviv/idealista-rental-yield-finder): gross rental yield, median price and rent per m² for a Spanish town and each of its districts, as one report per area instead of raw listings.
- [Property Listing Status Checker](https://apify.com/amitaviv/property-listing-status-checker): paste Fotocasa listing URLs you already have and learn which are still online, which are gone, and whose price changed.
- [Immobiliare.it Scraper](https://apify.com/amitaviv/immobiliare-it-scraper): the same kind of listing data for Italy, if you follow more than one Southern European market.

### Support

Open an issue on the actor's **Issues** tab with the run id.

# Actor input Schema

## `startUrls` (type: `array`):

Fotocasa search URLs copied from the browser's address bar, e.g. https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/todas-las-zonas/l?maxPrice=1500\&minRooms=2. Each URL is one search; several are fine. Make the search on fotocasa.es (town, district or neighbourhood, with any filters) and paste its URL. All property types (homes, garages, land, commercial premises, offices, storage rooms, buildings) and all deal types (sale, rent, share, holiday rent, business transfer, rent-to-own). The URL's filters and sort order are applied as they are. Use the list view (…/l); a plain map view (…/m) is read as the list of the whole place, and a map with a drawn or moved area is refused. Spanish, Catalan and English versions of the site are accepted; single listing pages (…/d) are not. Default: empty. Leave empty to search by "location" instead; both can be used in one run.

## `location` (type: `string`):

Instead of (or as well as) a URL: a Spanish town, district, neighbourhood or province as you would type it in Fotocasa's search box, e.g. Valencia, Chamberí, Gràcia Barcelona or Málaga provincia. Separate several with a semicolon (Valencia; Sevilla); each place is one search. The site's first suggestion is used and the run summary shows which place it matched ("Málaga" matches the province). Combined with the deal type, property type, price, bedrooms, bathrooms, size and order fields below. Default: empty.

## `transaction` (type: `string`):

Deal type for the places in "location" (a pasted URL keeps its own): sale (default), rent (long-term, monthly rent), share (room in a shared flat), holiday-rent, transfer (business transfer, traspaso) or rent-to-own.

## `propertyType` (type: `string`):

Property type for the places in "location" (a pasted URL keeps its own): home (default; flats, houses, penthouses, studios …), garage, land, commercial (shops and commercial premises), office, storage-room or building.

## `priceMin` (type: `integer`):

Lowest price in whole euros for the places in "location", e.g. 800: monthly rent for rentals, total price for sales. Default: no minimum. Not applied to pasted URLs.

## `priceMax` (type: `integer`):

Highest price in whole euros, inclusive, for the places in "location", e.g. 1500: monthly rent for rentals, total price for sales. Default: no maximum. A value below priceMin fails the run before any request.

## `roomsMin` (type: `integer`):

Minimum number of bedrooms for the places in "location", e.g. 2. Default: no minimum.

## `roomsMax` (type: `integer`):

Maximum number of bedrooms for the places in "location", e.g. 3. Default: no maximum. A value below roomsMin fails the run before any request.

## `bathroomsMin` (type: `integer`):

Minimum number of bathrooms for the places in "location", e.g. 2. Default: no minimum.

## `areaMin` (type: `integer`):

Minimum size in square metres (m²) for the places in "location", e.g. 60. Default: no minimum.

## `areaMax` (type: `integer`):

Maximum size in square metres (m²) for the places in "location", e.g. 120. Default: no maximum. A value below areaMin fails the run before any request.

## `sortBy` (type: `string`):

Order in which the places in "location" are read: relevance (Fotocasa's standard order, default), newest, cheapest or most-expensive. It decides which listings you get when maxItemsPerQuery is below the number of results. A pasted URL keeps its own order.

## `maxItemsPerQuery` (type: `integer`):

Maximum number of listings delivered for each search (each URL and each place), from 1 to 1,000,000; default 100. Apart from the start fee you pay only per delivered listing, so this also caps the cost. Fotocasa shows at most 9,900 listings of one search; set this higher to have a larger search read in price bands (see splitLargeSearches).

## `splitLargeSearches` (type: `boolean`):

Only matters when maxItemsPerQuery is above 9,900. On (default): a search with more listings than Fotocasa shows at once is cut into price bands that are read one after the other, cheapest first, so all of it can be delivered; listings without a price ("price on request") cannot be reached that way and are counted in the run summary. Off: only the first 9,900 listings of each search.

## `includePrivateSellerData` (type: `boolean`):

Off (default): for listings published by a private person (or by an advertiser whose type Fotocasa does not state), the name, phone, advertiser id and page are null; the address loses its house number and door, and the coordinates are rounded to about 100 m; phone numbers and e-mail addresses are removed from the description (privateDataRedacted: true). Names written inside the description are not removed. Agencies and developers are delivered in full either way. Switch on only if you have a lawful basis under the GDPR to process private persons' data (for example, their consent); by switching it on you confirm that you do.

## `onlyNew` (type: `boolean`):

Off (default): every run delivers the listings it finds. On: return and charge only listings that earlier runs of this actor in your account have not delivered for the same search, for scheduled monitoring. Searches are then read newest first and stop at the first page that holds a known listing. The first run delivers the newest listings up to the maximum and treats the rest of the page it read as seen; later runs deliver only listings that appeared since, so set maxItemsPerQuery above the number of new listings you expect between two runs. The memory is a key-value store named fotocasa-scraper-monitor in your account; delete it to start over.

## `monitorKey` (type: `string`):

Only with onlyNew. A free name for the monitor memory, e.g. madrid-daily. Runs with the same search and the same monitor name share one memory; give two schedules different names to keep their memories apart. Default: empty (one shared memory per search).

## `proxyConfiguration` (type: `object`):

Default and recommended: no proxy. Fotocasa's search service answered every request from Apify's own addresses in our tests. If a run reports that it was blocked, switch on Apify Proxy; each retry then goes out from a different address.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/todas-las-zonas/l?maxPrice=1500&minRooms=2"
  ],
  "transaction": "sale",
  "propertyType": "home",
  "sortBy": "relevance",
  "maxItemsPerQuery": 100,
  "splitLargeSearches": true,
  "includePrivateSellerData": false,
  "onlyNew": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All delivered listings as JSON, one object per listing. Main fields: id, url, transaction (sale, rent, share …), propertyType, propertySubtype, price (EUR; per month for rentals), pricePeriod, pricePerM2, previousPrice, priceDrop, priceDropPercent, sizeM2, rooms (bedrooms), bathrooms, floor, floorNumber, condition, has… feature flags, address, neighbourhood, district, municipality, province, zipCode, latitude, longitude, description, imageUrl, images, advertiserType (professional, private or unknown), advertiserName, advertiserPhone, privateDataRedacted, publishedAt (UTC), search, scrapedAt. A value Fotocasa does not give is null.

## `resultsCsv` (type: `string`):

All delivered listings as a CSV file, one row per listing, with the same fields as the JSON output (lists such as images are flattened into numbered columns).

## `summary` (type: `string`):

JSON record OUTPUT_SUMMARY: what was requested, delivered and charged; per search the place it matched, how many listings Fotocasa reports and how many price bands were read; which inputs failed or were ignored and why; why the run stopped.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/todas-las-zonas/l?maxPrice=1500&minRooms=2"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("amitaviv/fotocasa-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": ["https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/todas-las-zonas/l?maxPrice=1500&minRooms=2"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("amitaviv/fotocasa-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.fotocasa.es/es/alquiler/viviendas/madrid-capital/todas-las-zonas/l?maxPrice=1500&minRooms=2"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call amitaviv/fotocasa-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,amitaviv/fotocasa-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7eYOCLmbA90G8JC37/builds/rfyPMKggGmULUXfcJ/openapi.json
