# Fotocasa Scraper: Spain Property Listings API (`automatic/fotocasa-es-scraper`) Actor

Fotocasa API for Spanish real estate. Paste any fotocasa.es search URL or pick a location; get one clean record per home for sale or rent with price in EUR, m², bedrooms, bathrooms, address, photo and listing URL.

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

## Pricing

from $0.60 / 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.

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: Spain Property Listings API

Turn any Fotocasa search into a clean dataset. Paste a search URL from
`fotocasa.es`, with every filter you set in the browser, or simply type a
location and choose sale or rental. Each home comes back as one record
with price in euros, built area in m², bedrooms, bathrooms, address,
photo and the listing URL.

Use it as a Fotocasa API for the Spanish property market: schedule it,
call it from your code, or download the results as JSON, CSV or Excel.

### What you can do with it

- **Track asking prices in a city or district.** Re-run the same
  searches every day or week and follow asking prices, price per m² and
  the number of homes on the market in Madrid, Barcelona, Valencia,
  Málaga or any other Spanish location.
- **Find new listings before others do.** Sort by newest, run on a
  schedule and compare the stable `listingKey` with the previous run to
  spot new, removed and repriced homes.
- **Compare rentals and sales.** Run a sale search and a rental search
  for the same area and estimate gross rental yields per district.
- **Feed a valuation or investment model.** Price, area, bedrooms and
  bathrooms are typed numbers, ready for a spreadsheet, BI tool or
  database without manual cleanup.
- **Watch the agencies and developers in your area.** The `name` field
  carries the advertiser shown on the listing, so you can see who is
  listing what, where, and at which price.

### Quick start

1. Open [fotocasa.es](https://www.fotocasa.es/), search for a place and
   set the filters you want (price, bedrooms, area, terrace, lift,
   property type, sort order…).
2. Copy the address bar and paste it into **Fotocasa search URLs**.
3. Set **Maximum results** and run.

```json
{
  "startUrls": [
    {"url": "https://www.fotocasa.es/es/alquiler/viviendas/barcelona-capital/todas-las-zonas/l"}
  ],
  "maxPages": 2,
  "maxResults": 60
}
```

Prefer not to paste URLs? The original input still works exactly as
before:

```json
{"location": "madrid-provincia", "operation": "sale", "maxResults": 50}
```

### Input

| Name | Type | Default | What it does |
| --- | --- | --- | --- |
| `startUrls` | array | empty | Optional. Up to 10 Fotocasa search URLs with any filters. When set, these searches run instead of `location`, `zone`, `operation` and `sortType`. |
| `location` | string | `madrid-provincia` | Location to search, as used by Fotocasa (`barcelona-capital`, `valencia-capital`, `malaga-capital`…). Accents and spaces are converted for you ("Alcalá de Henares" becomes `alcala-de-henares`). |
| `zone` | string | `todas-las-zonas` | Zone within the location; the default covers the whole location. |
| `operation` | string | `sale` | Homes for sale (`sale`) or for rent (`rental`). |
| `sortType` | string | `scoring` | Order results by relevance (`scoring`), newest (`publication`), or price (`price-asc`, `price-desc`). |
| `maxPages` | integer | `1` | Result pages to read, from 1 to 5 (about 30 homes per page). With `startUrls`, per URL. |
| `maxResults` | integer | `50` | Maximum homes for the whole run, from 1 to 200. |
| `requestDelaySecs` | number | `1.0` | Pause between result pages, from 0.5 to 10 seconds. |

With `startUrls`, each search starts at its first results page and keeps
all of its filters, and a home that appears in two of your searches is
returned once.

### Input recipes

**Two-bedroom homes for sale in Madrid (pasted URL).** Fotocasa writes
many filters into the address itself, so the URL you copy already holds
them:

```json
{
  "startUrls": [
    {"url": "https://www.fotocasa.es/es/comprar/viviendas/madrid-provincia/todas-las-zonas/2-habitaciones/l"}
  ],
  "maxPages": 3,
  "maxResults": 90
}
```

**Several searches in one run.** Homes with a terrace in Madrid, flats
for sale in Valencia sorted from cheapest, and rentals in Málaga:

```json
{
  "startUrls": [
    {"url": "https://www.fotocasa.es/es/comprar/viviendas/madrid-provincia/todas-las-zonas/terraza/l"},
    {"url": "https://www.fotocasa.es/es/comprar/pisos/valencia-capital/todas-las-zonas/l?sortType=price&sortOrderDesc=false"},
    {"url": "https://www.fotocasa.es/es/alquiler/viviendas/malaga-capital/todas-las-zonas/l"}
  ],
  "maxPages": 2,
  "maxResults": 150
}
```

**Form fields: newest rentals in Barcelona.**

```json
{"location": "barcelona-capital", "operation": "rental", "sortType": "publication", "maxPages": 2, "maxResults": 60}
```

**Form fields: cheapest homes for sale in a province.**

```json
{"location": "toledo-provincia", "operation": "sale", "sortType": "price-asc", "maxResults": 30}
```

**Daily market monitor.** Save one of the inputs above as a task and add
a schedule (for example, every morning). Each run returns that day's
listings with the same fields, so the datasets line up over time.

### Output example

Each item includes `id`, `listingKey`, `url`, `name`, `price`,
`currency`, `areaM2`, `rooms`, `bedrooms`, `bathrooms`, `address`,
`imageUrl`, `operation`, `propertyType`, `portal`, `country`, `source`
and `scrapedAt`.

Example record (from a Madrid province sale search):

```json
{"id": "190289557", "listingKey": "fotocasa.es:190289557", "url": "https://www.fotocasa.es/es/comprar/vivienda/valdemoro/aire-acondicionado-calefaccion-parking-terraza-ascensor-no-amueblado/190289557/d", "name": "Hogares Valdemoro", "price": 249900.0, "currency": "EUR", "areaM2": 69.0, "rooms": 2, "bedrooms": 2, "bathrooms": 1, "address": "Estación, Valdemoro, Madrid", "imageUrl": "https://static.fotocasa.es/images/ads/4c1b51a3-a9c1-49f7-95f8-c3e97fb0394c", "operation": "sale", "propertyType": "viviendas", "portal": "fotocasa", "country": "ES", "source": "fotocasa.es", "scrapedAt": "2026-09-04T00:00:00+00:00"}
```

What the fields mean:

- `price` and `currency`: the advertised price as a number, always in
  EUR. For rentals this is the monthly rent. Homes listed with "Precio a
  consultar" have an empty price and currency.
- `areaM2`: the area in m² shown on the listing.
- `rooms` and `bedrooms`: the bedroom count (Fotocasa shows "hab."); both
  fields carry the same number. `bathrooms` is the bathroom count.
- `address`: neighborhood, district, town and province, as published.
  Many advertisers hide the exact street.
- `name`: the advertiser or listing title shown on the card.
- `operation` and `propertyType`: `sale` or `rental`, and the property
  type of the search (`viviendas` for form searches; a pasted URL gives
  its own type, such as `pisos` or `casas`).
- `listingKey`: a stable unique key, ideal for de-duplication and for
  tracking a home across runs.
- `scrapedAt`: when the run collected the record (UTC).

### More of the Spanish market

Fotocasa is one of Spain's largest property portals. Every record names
its `source` and `portal`, so you can merge this dataset with other
portal datasets and still filter by origin.

### FAQ

**Can I use any Fotocasa search URL?** Any search results page in
Spanish on `www.fotocasa.es` (addresses that start with `/es/comprar/`
or `/es/alquiler/` and end in `/l`). Location, zone, property type,
bedrooms, features such as terrace or pool, price filters and sort order
all carry over. Single listing pages, other languages and other sites
are rejected with a clear message.

**Do my saved inputs still work?** Yes. The location, zone, operation and
sort fields behave exactly as before. Search URLs are an optional extra.

**How fresh are the results?** Every run reads the live search pages, so
you get the homes that are online at that moment.

**Which locations are supported?** All of Spain: any province, city,
district or zone that Fotocasa covers.

**Why did my run stop with a location error?** The location must exist
on Fotocasa exactly as written in its search addresses, for example
`valencia-capital` or `valencia-provincia` rather than `valencia`.
Unknown locations stop the run instead of returning unrelated homes.
Copy the slug from the address bar, or paste the whole search URL.

**Can I collect rentals?** Yes. Set `operation` to `rental`, or paste an
`/es/alquiler/` search URL.

**Why is a field empty?** Some listings do not publish every detail (a
plot has no bedrooms, some homes have no public price). The field is
empty instead of guessed.

**How many homes can I get?** Up to 5 pages per search and 200 homes per
run. Add more search URLs, for example one per district, to cover a
larger area in one run.

**Do you collect phone numbers or emails?** No. The dataset holds the
public listing details only.

For help, contact Apify Support and include the run URL.

### Pricing

You pay per listing returned plus a small start event for each run.
The start event is charged per GB of memory, so the
default 512 MB run is one start event. See the Actor's Pricing tab on
Apify for current rates. Total run cost depends on the selected limits
and your Apify plan. Start with a small `maxResults` value and check the
run cost before scheduling larger collections.

# Actor input Schema

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

Paste up to 10 search URLs copied from your browser on fotocasa.es after setting any filters (price, rooms, area, property type, zone, sort order), e.g. https://www.fotocasa.es/es/alquiler/viviendas/barcelona-capital/todas-las-zonas/l. Each search starts at its first results page and keeps all its filters. When you paste URLs, Location, Zone, Operation and Sort order are ignored; Maximum pages applies to each URL and Maximum results to the whole run. Leave empty to search by the fields below.

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

Location to search, as it appears in Fotocasa search addresses (e.g. 'madrid-provincia', 'barcelona-capital', 'valencia-capital'). A location Fotocasa does not know stops the run with an error. Free text is slugified (lowercase, accents stripped, spaces to dashes); 'madrid' resolves to the whole province.

## `zone` (type: `string`):

Zone slug within the location; 'todas-las-zonas' (all zones) covers the whole location.

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

Listing operation: sale (comprar) or rental (alquiler).

## `sortType` (type: `string`):

Search-result ordering: scoring (relevance, default), publication (newest), price-asc or price-desc.

## `maxPages` (type: `integer`):

Search-result pages to read, about 30 listings per page. With pasted URLs this is per URL.

## `maxResults` (type: `integer`):

Stop after this many unique listings for the whole run.

## `requestDelaySecs` (type: `number`):

Pause between result pages, from 0.5 to 10 seconds.

## Actor input object example

```json
{
  "startUrls": [],
  "location": "madrid-provincia",
  "zone": "todas-las-zonas",
  "operation": "sale",
  "sortType": "scoring",
  "maxPages": 1,
  "maxResults": 50,
  "requestDelaySecs": 1
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("automatic/fotocasa-es-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": [] }

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

```

## MCP server setup

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