# Fotocasa Scraper (`normdata/fotocasa-scraper`) Actor

Scrape Fotocasa Spain homes for sale, rent and room shares: price, price drops, €/m², rooms, m², floor, condition, 60+ features, GPS, photos, agency and phone. Multiple locations, gets past the 10,000 cap. Optional details: energy rating, street, agency ref.

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

## Pricing

from $13.00 / 1,000 results

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

![Norm Data](https://raw.githubusercontent.com/FabriAV/normdata/refs/heads/main/assets/banner_norm2.png)

## 🏠 Fotocasa Scraper

Extract property listings from **Fotocasa**, one of Spain's biggest property portals. It covers homes **for sale**, **to rent**, **rooms to share**, **business transfers (traspasos)** and **rent to own**, plus garages, land, commercial premises, offices, storage rooms and whole buildings. Search any city, district, neighbourhood or province, or paste any Fotocasa link.

Each listing comes with:

- Price, **price drop and previous price**, and **€ per m²**.
- Bedrooms, bathrooms, m², floor, condition, building age and orientation.
- **60+ features** in plain English, plus ready-made yes/no columns.
- Full description, address, GPS, photos, videos and virtual tour.
- The **agency or private seller, with phone number**.

Turn on **Full details** to add the **energy rating** and consumption, emissions rating, street, **agency reference**, heating fuel, plot size, agency website and, for new builds, the **agency email**.

### 🎯 Who uses it?

#### 🏘️ Property investors

Pull every flat in your target neighbourhoods with €/m², price drops and condition, and spot undervalued listings. You can also filter for **occupied, tenanted, auction and bare-ownership** deals.

#### 📞 Lead generation and proptech sales

Get every agency active in a city with its phone number, website and listings. Or keep only **private sellers** to reach owners directly.

#### 📊 Analysts, valuers and researchers

Track asking prices, rents, €/m² and price cuts across Spanish cities over time.

#### 🔔 Buyers, renters and relocation agents

Schedule a search and receive only the listings added since the last run.

### ✨ What it does

- **Any place by name.** A city ("Valencia"), a district or neighbourhood ("Chamberí, Madrid", "Russafa, Valencia"), an area ("Costa del Sol Occidental - Zona de Marbella") or a whole province ("provincia de Málaga"). Several in one run.
- **Any link.** Paste Fotocasa search pages with all their filters, or single listings (new builds too).
- **Every operation:** for sale, to rent, room to share, business transfer, rent to own.
- **Every property type:** homes, garages, land, commercial premises, offices, storage rooms, buildings.
- **Fotocasa filters:**
  - Price, bedrooms, bathrooms and size (min and max).
  - Home and land types (flat, penthouse, duplex, studio, house, buildable land...).
  - Condition, and up to 25 must-have features combined.
  - Highlights (luxury, beach views, seafront, fireplace, villa...).
  - Occupancy (vacant, occupied, rented, auction, bare ownership).
  - Published in the last 24h / 48h / week / month, keyword search and 7 sort orders.
- **Extra filters Fotocasa doesn't have:** price drops only, private sellers only.
- **New listings only.** For scheduled runs: saves only what was added since the last run of the same search.
- **Full details (optional):** energy rating and kWh/m², emissions rating and kg CO2/m², street and number, agency reference, heating fuel, plot size, agency email (new builds), website and logo, tourist licence.

### 🏆 Why this scraper

- **Fast.** About 1,000 listings in 15 to 20 seconds. With full details, 1,000 listings in about 5 minutes.
- **Complete.** Fotocasa stops paging after about 10,000 results per search. This Actor splits big searches into price bands, so a whole city comes back in full: 11,941 of Madrid's 12,076 homes for sale in about 6 minutes.
- **Ready to analyse.**
  - Prices are numbers, €/m² and the previous price are already worked out, and dates are in ISO format.
  - Fotocasa's codes are translated into plain English: floors, condition, building age, orientation and features.
  - Each feature also has its own true/false column (lift, parking, pool, terrace...).
- **Phone on almost every row.** No extra step or add-on needed.
- **No proxy needed.** Nothing extra to set up or pay for.

### ⚖️ How it compares

Based on each Actor's public input options (October 2026).

| Capability | This actor | igolaizola | memo23 | azzouzana |
|---|:--:|:--:|:--:|:--:|
| Search by place name | **yes** | yes | yes | no (link only) |
| Several places by name in one run | **yes** | no | no | no |
| Search links with their filters | **yes** | no | yes | yes |
| Single listing links | **yes** | no | no | no |
| Bedrooms filter | **yes** | no | yes | no |
| Bathrooms filter | **yes** | no | no | no |
| Several home types at once (flat, penthouse, duplex...) | **yes** | no | one at a time | no |
| Several must-have features at once | **yes** | no | one at a time | no |
| Condition filter | **yes** | no | partly | no |
| Occupancy (occupied, rented, auction, bare ownership) | **yes** | no | no | no |
| Keyword search | **yes** | no | no | no |
| Price drops only | **yes** | no | no | no |
| Private sellers only | **yes** | no | no | no |
| Radius around a point | no | yes | yes | no |
| New listings only (monitoring) | **yes** | no | yes | no |
| Full listing details | **yes** | no | yes | yes |

### 📦 What data you get

| Field | Description |
| --- | --- |
| `id`, `url` | Fotocasa ID and listing page. |
| `operation` | `sale`, `rent`, `share`, `transfer` or `rent-to-own`. |
| `propertyType`, `propertySubtype` | "Home", "Garage", "Land", "Office"...; "Flat", "Penthouse", "Duplex", "Buildable land"... |
| `price`, `currency`, `pricePeriod` | Price in € (`EUR`); `month` for rentals and room shares. |
| `previousPrice`, `priceDrop` | Price before the last cut and the cut in €, when the price was dropped. |
| `pricePerM2` | € per m² when the size is known. |
| `rooms`, `bathrooms`, `sizeM2` | Bedrooms, bathrooms and built size. |
| `floor`, `floorNumber` | "3rd floor", "Ground floor", "Penthouse"... and the floor as a number. |
| `condition`, `buildingAge`, `orientation`, `hotWater` | "Very good", "Needs renovation"...; "20 to 30 years"...; "South"...; "Natural gas"... |
| `isExterior`, `hasLift`, `hasParking`, `hasPool`, `hasGarden`, `hasTerrace`, `hasBalcony`, `hasAirConditioning`, `hasHeating`, `hasStorageRoom`, `isFurnished` | Ready-made yes/no columns. |
| `features`, `tags` | Every feature in English ("Lift", "Fitted kitchen", "Sea views"...) and Fotocasa highlights ("Luxury", "Exterior"...). |
| `description` | Full listing text. |
| `address`, `neighborhood`, `district`, `city`, `county`, `province`, `region`, `zipCode` | Location. |
| `latitude`, `longitude`, `exactLocation` | GPS, and whether it's the exact address or an approximate point. |
| `searchedLocation` | Which of your places or links found it. |
| `image`, `images`, `imageCount` | Main photo and every photo in full size. |
| `videoUrls`, `virtualTourUrl`, `floorPlanUrls` | Videos, 3D tour and floor plans. |
| `advertiserType`, `agencyName`, `agencyId`, `agencyUrl`, `phone` | `agency` or `private`, the agency and its Fotocasa page, and the phone number. |
| `isNewConstruction`, `developmentName`, `developmentUrl` | New builds and their development. |
| `isOccupied`, `isRentedWithTenants`, `isAuction`, `isBareOwnership`, `isTemporaryRental`, `isFeatured` | Listing flags. |
| `listedAt` | When it was published. |
| `scrapedAt` | When the row was collected (UTC). |

With **Full details** on, each row also has: `agencyReference`, `street`, `streetNumber`, `energyRating`, `energyConsumptionKwhM2`, `emissionsRating`, `emissionsKgCo2M2`, `heatingFuel`, `plotSizeM2`, `rentReferenceIndex`, `agencyEmail`, `agencyWebsite`, `agencyLogo`, `touristLicence`.

An `error` field appears only when a listing's details didn't load. The rest of that row is complete.

#### Example row (trimmed, with Full details)

```json
{
  "id": 190374775,
  "url": "https://www.fotocasa.es/es/comprar/vivienda/valencia-capital/aire-acondicionado-calefaccion-ascensor-no-amueblado/190374775/d",
  "operation": "sale",
  "propertyType": "Home",
  "propertySubtype": "Flat",
  "price": 475000,
  "currency": "EUR",
  "previousPrice": 495000,
  "priceDrop": 20000,
  "pricePerM2": 4896.91,
  "rooms": 2,
  "bathrooms": 2,
  "sizeM2": 97,
  "floor": "3rd floor",
  "floorNumber": 3,
  "condition": "Very good",
  "buildingAge": "Over 100 years",
  "orientation": "South",
  "hasLift": true,
  "hasAirConditioning": true,
  "features": ["Air conditioning", "Balcony", "En-suite bathroom", "Heating", "Lift", "Unfurnished"],
  "tags": ["Exterior", "Modern"],
  "address": "Carrer de Buenos Aires, Russafa",
  "neighborhood": "Russafa",
  "district": "L'Eixample",
  "city": "Valencia",
  "province": "Valencia",
  "zipCode": "46006",
  "latitude": 39.46144802481423,
  "longitude": -0.377551565598776,
  "imageCount": 37,
  "virtualTourUrl": "https://floorfy.com/tour/2681905",
  "advertiserType": "agency",
  "agencyName": "Engel & Völkers València",
  "phone": "+34963517897",
  "listedAt": "2026-07-31T13:50:56.107Z",
  "agencyReference": "I-04ARLH-W-04ARLH",
  "street": "Carrer de Buenos Aires",
  "energyRating": "D",
  "energyConsumptionKwhM2": 73,
  "emissionsRating": "D",
  "emissionsKgCo2M2": 15,
  "heatingFuel": "Natural gas",
  "scrapedAt": "2026-10-04T16:51:35.554Z"
}
```

### 💡 Use cases

#### 🏚️ Every price-cut flat in Madrid that needs renovation

```json
{ "locations": ["Madrid"], "subtypes": ["flat"], "condition": ["needs_renovation"], "priceDropOnly": true, "maxItems": null }
```

#### 💼 Investor deals: tenanted and auction properties in Barcelona province

```json
{ "locations": ["provincia de Barcelona"], "occupancy": ["rented", "auction"], "maxItems": null }
```

#### 📞 Private landlords renting in Valencia, with phones

```json
{ "locations": ["Valencia"], "operation": "rent", "privateOnly": true, "maxItems": null }
```

#### 🔔 New 2-bedroom rentals with lift in three neighbourhoods, daily

```json
{ "locations": ["Chamberí, Madrid", "Salamanca, Madrid", "Retiro, Madrid"], "operation": "rent", "minRooms": 2, "features": ["lift"], "onlyNew": true, "maxItems": null }
```

Schedule it every morning: each run saves only the listings added since the day before.

#### 🌊 Luxury homes with beach views on the Costa del Sol, with energy ratings

```json
{ "locations": ["Marbella", "Estepona"], "tags": ["luxury", "sea_views"], "includeDetails": true, "maxItems": 200 }
```

#### 🔗 Your own Fotocasa search, as it is

```json
{ "urls": ["https://www.fotocasa.es/es/alquiler/viviendas/barcelona-capital/todas-las-zonas/l?minRooms=2&maxPrice=1500"], "maxItems": null }
```

### ⚙️ How the input is organised

| Section | What it's for |
| --- | --- |
| **Maximum properties** | Always first: caps the whole run. Empty means no limit. |
| **Where** | Places, Fotocasa links, operation and property type. |
| **Filters** | Price, rooms, bathrooms, size, types, condition, must have, highlights, occupancy, published, keywords, price drops, private sellers, sort, new listings only. |
| **What to include** | Full details (paid add-on). |

For rentals, prices are per month. Links keep their own filters; the form filters apply to the places you type.

### 💰 Pricing

Pay-per-event: a one-time **$0.003** when the run starts, plus a price per property saved to your dataset. The price depends on your Apify plan tier:

| Your Apify plan | Price per property |
| --- | --- |
| Free | $0.019 |
| Bronze | $0.017 |
| Silver | $0.015 |
| Gold / Platinum / Diamond | $0.013 |

Approximate total cost:

| Properties collected | Free | Bronze | Silver | Gold+ |
| --- | --- | --- | --- | --- |
| 100 | $1.30 | $1.20 | $1.10 | $1.00 |
| 1,000 | $19.00 | $17.00 | $15.00 | $13.00 |
| 10,000 | $130.00 | $120.00 | $110.00 | $100.00 |

**Full details add-on.** You pay this only when **Full details** is on, once for each property saved with its details:

| Your Apify plan | Add-on per property |
| --- | --- |
| Free | $0.005 |
| Bronze | $0.004 |
| Silver | $0.0035 |
| Gold / Platinum / Diamond | $0.003 |

Leave it off and you pay only the base price. There are no proxy costs. New Apify accounts start with $5 in free credit. On the Free plan a run saves up to 10 properties.

**Speed:**

- About 1,000 listings in 15 to 20 seconds.
- A whole city of about 12,000 in about 6 minutes.
- With full details, about 1,000 listings in 5 minutes.

### 🚀 Run it

1. Create a free Apify account with $5 in credit.
2. Open the Fotocasa Scraper.
3. Type a place (or keep the prefilled one), or paste Fotocasa links, add filters, then click **Start**.
4. Export the results as CSV, Excel, JSON, or XML from the Dataset tab.

You can also:

- Run it programmatically through the Apify API (`run-sync-get-dataset-items`) or the ApifyClient for JavaScript and Python.
- Schedule it with Apify's built-in Scheduler, with **New listings only** on, to catch new listings every day.

### 🤖 Use with AI agents (MCP)

Give an AI agent live access to Fotocasa listings through the Model Context Protocol:

```
claude mcp add --transport http apify "https://mcp.apify.com?tools=normdata/fotocasa-scraper"
```

Then prompt it in plain language to run the scraper and read back the results.

### 🔧 Troubleshooting

**A place was skipped.**
Type it the way Fotocasa's search box shows it: "Gràcia, Barcelona", "Playa de Gandia, Gandia". Or paste the search link instead. Whole regions such as "Andalucía" aren't in Fotocasa's location box: list the provinces instead ("provincia de Málaga", "provincia de Sevilla").

**"Madrid" gave me only the city.**
That's how Fotocasa reads it too. Type "provincia de Madrid" for the whole province.

**The log says Fotocasa asked to slow down.**
On very big runs Fotocasa limits how fast pages load. The Actor pauses for a minute and carries on by itself.

**Some listings have no energy rating.**
Many advertisers don't publish the certificate. It's filled in when they do.

**A scheduled "New listings only" run saved nothing.**
Nothing new was listed since the last run of the same search. Changing the places, links or filters starts a fresh memory; set a **Monitor name** to keep it.

### ❓ FAQ

| Question | Answer |
| --- | --- |
| Do I need a Fotocasa account? | No. Only public listings are read. |
| Does it need a proxy? | No. |
| Can I get more than 10,000 results? | Yes. Leave the maximum empty: big searches are split into price bands automatically. |
| Can I get the phone number? | Yes, `phone` on almost every listing. |
| Can I get rentals, room shares and commercial property? | Yes. Pick them in **Operation** and **Property type**. |
| Can I track new listings? | Yes. Turn on **New listings only** and schedule the run. |
| Does it do holiday rentals or radius searches? | Not yet. Write to us if you need them. |

### 🛡️ Limits & responsible use

This Actor reads only public listings from Fotocasa. It never signs in and never contacts advertisers. Private sellers' phone numbers are personal data: handle them in line with the GDPR and Spain's LOPDGDD, and use the data in line with Fotocasa's terms.

### 📧 Contact

Need a scraper for a different site, or found something wrong with this one? norm.data.scrapers@gmail.com

### 🧪 Local development

```powershell
bun install
bun run typecheck
bun test/qa.mjs
apify run
```

Local results are stored in `storage/datasets/default`.

# Actor input Schema

## `maxItems` (type: `integer`):

Stop after this many properties. Leave empty for no limit (every listing that matches, even past Fotocasa's 10,000-result cap). The Apify Free plan is always capped at 10.

## `locations` (type: `array`):

Anything you would type in the Fotocasa search box: a city ("Madrid", "Málaga"), a district or neighbourhood ("Chamberí, Madrid", "Gràcia, Barcelona"), a county or area ("Costa del Sol Occidental - Zona de Marbella") or a whole province ("provincia de Valencia"). "Madrid" alone is the city.

## `urls` (type: `array`):

Fotocasa search pages with all their filters (e.g. fotocasa.es/es/alquiler/viviendas/barcelona-capital/todas-las-zonas/l?minRooms=2) or single listings (fotocasa.es/es/comprar/vivienda/.../190783970/d). Links keep their own filters; the filters below apply to Locations.

## `operation` (type: `string`):

Buy, rent, room shares, business transfers or rent-to-own.

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

Homes, garages, land, commercial premises, offices, storage rooms or whole buildings.

## `minPrice` (type: `integer`):

For rentals: per month.

## `maxPrice` (type: `integer`):

For rentals: per month.

## `minRooms` (type: `integer`):

0 = studio.

## `maxRooms` (type: `integer`):

Leave empty for no limit.

## `minBathrooms` (type: `integer`):

Leave empty for any.

## `maxBathrooms` (type: `integer`):

Leave empty for no limit.

## `minSize` (type: `integer`):

Built size.

## `maxSize` (type: `integer`):

Built size.

## `subtypes` (type: `array`):

Keep only these kinds. Leave empty for all. Home kinds apply to Homes, land kinds to Land.

## `condition` (type: `array`):

Keep only listings in ANY of these states.

## `features` (type: `array`):

Keep only listings with ALL of these. The last ones apply to rentals, room shares or commercial premises.

## `tags` (type: `array`):

Keep only listings Fotocasa tags with ANY of these.

## `occupancy` (type: `array`):

Keep only listings in ANY of these situations, e.g. bank and investor deals: occupied, rented with tenants, auctions, bare ownership.

## `publishedWithin` (type: `string`):

Only listings published (or renewed) in this window.

## `keywords` (type: `string`):

Fotocasa's own text search over the listing, e.g. "reformado", "vistas al mar", "garaje".

## `priceDropOnly` (type: `boolean`):

Keep only listings whose price has been cut.

## `privateOnly` (type: `boolean`):

Keep only listings posted by owners, not agencies.

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

Order of the results. Matters most when you set a Maximum.

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

For scheduled runs: save only listings this same search has not saved before. The first run saves everything; later runs save just what was added since. Kept per search (locations, links and filters).

## `monitorName` (type: `string`):

Optional. Give "New listings only" a fixed name to keep its memory even after you change the filters.

## `includeDetails` (type: `boolean`):

Reads each full listing and adds: energy rating and consumption, emissions rating, street and number, agency reference, heating fuel, plot size, agency email (new builds), website and logo, tourist licence and rent reference index. Paid add-on: one extra charge per property saved with details (see Pricing).

## Actor input object example

```json
{
  "maxItems": 10,
  "locations": [
    "Madrid"
  ],
  "operation": "sale",
  "propertyType": "homes",
  "priceDropOnly": false,
  "privateOnly": false,
  "sortBy": "relevance",
  "onlyNew": false,
  "includeDetails": false
}
```

# Actor output Schema

## `properties` (type: `string`):

No description

# 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 = {
    "maxItems": 10,
    "locations": [
        "Madrid"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("normdata/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 = {
    "maxItems": 10,
    "locations": ["Madrid"],
}

# Run the Actor and wait for it to finish
run = client.actor("normdata/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 '{
  "maxItems": 10,
  "locations": [
    "Madrid"
  ]
}' |
apify call normdata/fotocasa-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,normdata/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/ZwNjj4LsUZz5fwSGi/builds/Y8tdtXPV0WG4IZ3Pe/openapi.json
