# Fotocasa All-in-One Scraper — Spain Buy & Rent Listings (`khadinakbar/fotocasa-all-in-one-scraper`) Actor

Scrape fotocasa.es Spain real estate by city or search URL. Buy, rent, share & transfer. Price, m², rooms, baths, GPS, agency phone, images. HTTP-only, MCP-ready, no login.

- **URL**: https://apify.com/khadinakbar/fotocasa-all-in-one-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 property scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 All-in-One Scraper — Spain Buy & Rent Listings

Fotocasa All-in-One Scraper is an Apify Actor for collecting public Spain real-estate listings from Fotocasa.es. It accepts a city or area name, or a pasted Fotocasa search or detail URL, and returns one structured record per listing. Each record can include price, surface in m², rooms, bathrooms, floor, city, neighborhood, ZIP, GPS coordinates, agency name, agency phone, image count, listing timestamps, and the canonical listing URL. It runs over HTTP with Apify Residential Spain proxies and is usable through Apify MCP.

### Best fit and connected workflows

This Actor fits workflows centered on Spanish residential inventory from Fotocasa:

- Buy-side research from `comprar` searches, with price, m², rooms, and agency contact fields
- Rental research from `alquiler` searches, with monthly rent and size fields
- URL-driven collection when you already filtered on Fotocasa and want the Actor to follow that search
- Location-driven collection when you want the Actor to resolve a city such as Madrid or Barcelona and run buy or rent filters
- CRM and spreadsheet import where one flat row per listing is the next step

If your workflow centers on another property market, these verified related Actors provide natural routing options:

- Then use [Idealista Scraper](https://apify.com/khadinakbar/idealista-scraper) to extend Fotocasa All-in-One Scraper with a neighboring Spain/Italy/Portugal Idealista source when the brief calls for Idealista data
- Then use [Rightmove All-in-One Scraper](https://apify.com/khadinakbar/rightmove-all-in-one-scraper) to extend Fotocasa All-in-One Scraper with a neighboring UK BUY/RENT/SOLD source when the brief calls for Rightmove data
- Then use [Zillow All-in-One Scraper](https://apify.com/khadinakbar/zillow-all-in-one-scraper) to extend Fotocasa All-in-One Scraper with a neighboring US property source when the brief calls for Zillow data

Fotocasa All-in-One Scraper — Spain Buy & Rent Listings is designed as a focused standalone workflow for Fotocasa Spain.

### Practical scenario

Lucia is comparing Madrid Capital buy listings for a relocation client. She sets `location` to `Madrid`, `operation` to `buy`, and `maxItems` to `10`. The dataset returns fields such as `price`, `surfaceM2`, `rooms`, `bathrooms`, `neighborhood`, `agencyPhone`, and `url`. Lucia sorts by €/m² using `pricePerM2`, shortlists three districts, and opens each `url` to review photos before sharing a shortlist with the client.

### Input

Use either a Fotocasa URL in `startUrls` or a location-based search with `location` and `operation`.

| Field | Type | Purpose |
|---|---|---|
| `startUrls` | array | Fotocasa search (`…/l`) or detail (`…/{id}/d`) URLs. Search URLs are paginated until `maxItems`. |
| `location` | string | Spanish city, province, or area when `startUrls` is empty (for example Madrid or Barcelona). |
| `locationType` | string | Optional suggestion level when several matches exist (municipality vs province). |
| `operation` | string | `buy`, `rent`, `share`, `rentBuyOption`, or `transfer` for location mode. |
| `propertyType` | string | Category such as `home`, `office`, `garages`, or `land`. |
| `minPrice` / `maxPrice` | integer | EUR filters (monthly rent for rent mode). |
| `minSize` / `maxSize` | integer | Floor-area filters in m². |
| `fetchDetails` | boolean | Optional detail-page enrich for energy-certificate attempts when present. |
| `maxItems` | integer | Cap on returned rows and property event charges. |
| `proxyConfiguration` | object | Prefer Apify Residential Spain for stable yield. |

Focused input example:

```json
{
  "location": "Madrid",
  "operation": "buy",
  "propertyType": "home",
  "maxItems": 10,
  "fetchDetails": false
}
```

### Output

Each dataset item is one flat JSON record per listing.

| Field | Type | Meaning |
|---|---|---|
| `id` | string | Fotocasa property identifier. |
| `url` | string | Canonical listing URL. |
| `operation` | string | Buy, rent, or related operation label. |
| `title` | string or null | Public listing title. |
| `price` | number or null | Asking price in EUR. |
| `currency` | string | Usually EUR. |
| `rooms` | integer or null | Room count when published. |
| `bathrooms` | integer or null | Bathroom count when published. |
| `surfaceM2` | number or null | Floor area in square metres. |
| `pricePerM2` | number or null | Derived €/m² when price and size exist. |
| `floor` | number or null | Floor level when published. |
| `city` | string or null | City or municipality label. |
| `neighborhood` | string or null | Neighborhood label. |
| `zipCode` | string or null | Postal code when published. |
| `latitude` / `longitude` | number or null | GPS coordinates when published. |
| `agencyName` | string or null | Agency or advertiser name. |
| `agencyPhone` | string or null | Public agency phone when shown. |
| `imageCount` | integer or null | Number of listing images. |
| `listedAt` | string or null | Listing timestamp when published. |
| `scrapedAt` | string | Collection timestamp for the row. |

Illustrative JSON record:

```json
{
  "id": "189625641",
  "url": "https://www.fotocasa.es/es/comprar/vivienda/madrid-capital/example/189625641/d",
  "operation": "buy",
  "title": "Alcalá, Barrio de Salamanca",
  "price": 1620000,
  "currency": "EUR",
  "rooms": 3,
  "bathrooms": 2,
  "surfaceM2": 130,
  "pricePerM2": 12461.54,
  "city": "Madrid Capital",
  "neighborhood": "Goya",
  "zipCode": "28006",
  "latitude": 40.427532,
  "longitude": -3.6726725,
  "agencyName": "ESPÍGUL IMMOBILIARIA",
  "agencyPhone": "+34913439848",
  "imageCount": 22,
  "listedAt": "2026-05-08T15:08:35.443Z",
  "scrapedAt": "2026-09-15T19:00:00.000Z"
}
```

### How it works

The Actor resolves a city through Fotocasa's public suggest API, then pages the property-search API until `maxItems` is reached. Pasted search URLs are resolved through location metadata when HTML SSR payloads are empty behind residential proxies, then the same property-search path fills the dataset. Optional `fetchDetails` follows listing pages for best-effort energy fields. Every terminal run writes `OUTPUT` and `RUN_SUMMARY` with an explicit outcome such as `COMPLETE`, `VALID_EMPTY`, or `INVALID_INPUT`.

### Pricing

This Actor uses Apify Pay per event plus standard Apify platform usage (passed through to the caller). Charged events:

- Actor start — $0.00005
- Property scraped — $0.005 per returned search listing
- Property detailed — $0.008 per detail-enriched listing when `fetchDetails` is on

Open the live Pricing tab in the Apify Console for the current source of truth. A run that returns ten search listings charges the start event once and the property-scraped event ten times, plus platform usage for compute and proxy.

### Use with AI agents (MCP)

This Actor is available through Apify MCP as `apify--fotocasa-all-in-one-scraper`. Exact Actor identity: `khadinakbar/fotocasa-all-in-one-scraper`.

Tool shape: resolve a Spain city or Fotocasa URL, return flat listing rows with price, size, rooms, agency phone, GPS, and URL for CRM or agent reasoning.

> Search Fotocasa for homes for sale in Madrid, return price, surfaceM2, rooms, bathrooms, neighborhood, agencyPhone, and url for each listing. Cap at 10 rows and summarize €/m² from pricePerM2.

Agent readback guidance:

- Dataset rows are the primary result store
- `OUTPUT` / `RUN_SUMMARY` carry the run outcome and counts
- `url` plus `scrapedAt` keep provenance inspectable
- `maxItems` bounds both yield and PPE charges
- Cost maps one returned search row to one `property-scraped` event; check the Pricing tab for live amounts alongside platform usage

### Apify API example

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor("khadinakbar/fotocasa-all-in-one-scraper").call({
  location: "Barcelona",
  operation: "rent",
  maxItems: 10,
  fetchDetails: false
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Best results and outcome guidance

Prefer Residential Spain proxies (the default). Segment large cities by district or price band instead of one oversized cap. Paste a ready-made Fotocasa search URL when filters are already set on the site. Leave `fetchDetails` off unless you specifically need detail-page energy attempts. Empty inventory for niche operations such as share can finish as `VALID_EMPTY` with zero property charges.

### Design note

I found that Fotocasa search HTML served through Residential often ships without `window.__INITIAL_DATA__`, so URL mode that only scraped SSR would look empty even when inventory existed. The durable path is: extract the location slug from the URL, resolve it through the suggest API, then page `web.gw.fotocasa.es/v2/propertysearch/search` — that is what keeps buy and rent URL runs complete on the private release build.

### FAQ

**How do I scrape rentals instead of sales?**\
Set `operation` to `rent`, or paste an `alquiler` search URL into `startUrls`.

**How do I scrape a single listing?**\
Paste a detail URL ending in `/{id}/d` into `startUrls`.

**How do I keep agency outreach fields?**\
Use the default search path; `agencyName` and `agencyPhone` are returned when Fotocasa publishes them on the card.

**How do I bound cost?**\
Set `maxItems` to the number of listings you need; each accepted search row maps to one property-scraped event.

### Responsible use

This Actor collects public listing fields from Fotocasa.es. Callers are responsible for complying with Fotocasa terms, Spanish and EU law, and their own data-protection obligations. Keep request volume appropriate to the research scope, and review agency contact fields before any outreach automation.

Fotocasa and Fotocasa.es are trademarks of their respective owners. This independent Actor is not affiliated with, associated with, or endorsed by Fotocasa or Adevinta Spain.

# Changelog

This Actor's version history is a separate document: https://apify.com/khadinakbar/fotocasa-all-in-one-scraper/changelog.md

# Actor input Schema

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

Fotocasa.es search or detail URLs to scrape. Search URLs look like https://www.fotocasa.es/es/comprar/viviendas/madrid-capital/todas-las-zonas/l and are auto-paginated. Detail URLs end with /{id}/d. When set, location/operation filters below are ignored for those URLs. Only fotocasa.es is supported.

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

Spanish city, province, or area when Start URLs is empty (e.g. Madrid, Barcelona, Valencia). Resolved via Fotocasa's public suggest API to combinedLocationIds. Ignored when Start URLs is set.

## `locationType` (type: `string`):

Optional Fotocasa suggestion level when several matches exist (e.g. municipality vs province for Madrid). Leave empty to use the top suggestion.

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

Buy or rent (and related) when using Location mode. Ignored when Start URLs already encode comprar/alquiler. Defaults to buy.

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

Fotocasa property category for Location mode. home = viviendas (default). Ignored when Start URLs is set.

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

Minimum asking price in EUR. For rent this is monthly rent. Leave empty for no minimum. Used in Location mode; also applied when a Start URL has no minPrice query.

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

Maximum asking price in EUR. For rent this is monthly rent. Leave empty for no maximum.

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

Minimum floor area in square metres. Leave empty for any size.

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

Maximum floor area in square metres. Leave empty for any size.

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

Sort order for Location-mode searches. rating = Fotocasa relevance score (default).

## `fetchDetails` (type: `boolean`):

Open each listing detail page to attempt energy-certificate enrichment. Slower and charges the property-detailed event instead of property-scraped. Default false — search cards already include price, m², rooms, agency phone, and images.

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

Hard cap on dataset rows and named PPE charges for this run. Each search page returns about 30 listings. Defaults to 50. Maximum 5000.

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

Apify proxy settings. Default is Residential Spain — Fotocasa often blocks datacenter IPs. Keep Residential ES unless you have a reason to change it.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.fotocasa.es/es/comprar/viviendas/madrid-capital/todas-las-zonas/l"
    }
  ],
  "location": "Madrid",
  "locationType": "",
  "operation": "buy",
  "propertyType": "home",
  "sortBy": "rating",
  "fetchDetails": false,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}
```

# Actor output Schema

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

Default dataset with one Fotocasa property per row.

## `OUTPUT` (type: `string`):

Terminal outcome record (COMPLETE, VALID\_EMPTY, INVALID\_INPUT, UPSTREAM\_FAILED).

## `RUN_SUMMARY` (type: `string`):

Same terminal outcome as OUTPUT for agent-friendly readback.

# 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": [
        {
            "url": "https://www.fotocasa.es/es/comprar/viviendas/madrid-capital/todas-las-zonas/l"
        }
    ],
    "location": "Madrid",
    "operation": "buy",
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "ES"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/fotocasa-all-in-one-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": [{ "url": "https://www.fotocasa.es/es/comprar/viviendas/madrid-capital/todas-las-zonas/l" }],
    "location": "Madrid",
    "operation": "buy",
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "ES",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/fotocasa-all-in-one-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": [
    {
      "url": "https://www.fotocasa.es/es/comprar/viviendas/madrid-capital/todas-las-zonas/l"
    }
  ],
  "location": "Madrid",
  "operation": "buy",
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}' |
apify call khadinakbar/fotocasa-all-in-one-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/fotocasa-all-in-one-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/PJ9KIV5oBR6Ececyu/builds/q2aKwGrvUmVJW4fPu/openapi.json
