# Fotocasa Property Listings Scraper (`automation-lab/fotocasa-property-listings-scraper`) Actor

Extract Fotocasa Spain listings with prices, features, images, locations, publication dates, and public advertiser contacts.

- **URL**: https://apify.com/automation-lab/fotocasa-property-listings-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Real estate
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Fotocasa Property Listings Scraper

Extract structured sale and rental listings from Fotocasa Spain.

Get canonical property URLs, EUR prices, descriptions, publication dates, rooms, bathrooms, surface area, images, location context, coordinates, and public advertiser contact details.

The Actor uses Fotocasa's structured search response rather than rendering property pages.
This keeps runs focused on useful listing data while supporting repeatable inventory snapshots.

### What does this Fotocasa scraper do?

Fotocasa Property Listings Scraper turns one or more Fotocasa location searches into a clean Apify dataset.

It can:

- collect homes for sale;
- collect rental listings;
- paginate through recent search inventory;
- enforce price, room, bathroom, surface, and property-type filters;
- deduplicate listings by stable Fotocasa ID;
- preserve the input search context on every row;
- export JSON, CSV, Excel, XML, or RSS through Apify;
- feed scheduled monitoring and property-comps workflows.

No Fotocasa login, cookie, or user-supplied source API key is required.
The Actor uses Spain residential proxy traffic because Fotocasa blocks datacenter requests.

### Who is it for?

#### Proptech and data teams

Snapshot inventory by Spanish market and compare new, removed, or repriced listings over time.

#### Property investors

Build comparable-property datasets using price, rooms, bathrooms, surface, coordinates, and description fields.

#### Brokers and agencies

Monitor competing inventory and aggregate publicly advertised agency information.

#### Valuation analysts

Export normalized inputs for price-per-square-metre analysis and market reports.

#### Lead operations teams

Route public advertiser records into compliant review and enrichment workflows.
Do not use public phone data for unsolicited outreach that violates law or platform terms.

### Why use this Actor?

- **Structured results:** no raw HTML cleanup is required.
- **Useful contacts:** advertiser identity and publicly displayed phone are included when Fotocasa returns them.
- **Consistent filters:** the same post-normalization filters apply to URL and location-ID inputs.
- **Stable deduplication:** duplicate IDs across pages or searches are saved once per run.
- **Bounded execution:** `maxItems` and `maxPagesPerSearch` keep runs predictable.
- **Integration ready:** results use the default dataset for standard Apify clients and webhooks.

### What Fotocasa data can I extract?

| Field | Meaning |
| --- | --- |
| `listingId` | Stable Fotocasa listing ID |
| `listingUrl` | Canonical public detail URL |
| `typeId` | Fotocasa property type ID |
| `subtypeId` | Fotocasa subtype ID |
| `transactionTypeId` | Source transaction ID |
| `transaction` | Normalized `sale` or `rent` value |
| `price` | Advertised price |
| `currency` | `EUR` |
| `reducedPrice` | Whether Fotocasa marks the price as reduced |
| `title` | Compact generated location/room label |
| `description` | Public listing description |
| `publishedAt` | Source publication timestamp |
| `location` | Fotocasa location hierarchy and IDs |
| `address` | Public area/locality label |
| `postalCode` | Postal code when exposed |
| `latitude` | Public approximate latitude |
| `longitude` | Public approximate longitude |
| `rooms` | Number of rooms when exposed |
| `bathrooms` | Number of bathrooms when exposed |
| `surfaceM2` | Advertised surface in square metres |
| `features` | Normalized Fotocasa feature map |
| `advertiser` | Public agency/client identity and phone |
| `imageUrls` | Public image URLs returned by search |
| `searchContext` | Input URL, location IDs, page, and filters |
| `scrapedAt` | Actor extraction timestamp |

Nullable fields remain `null` when Fotocasa does not provide them.
The Actor does not invent missing values.

### How to scrape Fotocasa listings

1. Open the Actor in Apify Console.
2. Choose sale (`1`) or rent (`3`).
3. Enter one or more Fotocasa `combinedLocationIds` values.
4. Alternatively, paste a Fotocasa URL containing `combinedLocationIds`.
5. Add optional property filters.
6. Set the maximum number of listings.
7. Keep the default Residential/Spain proxy configuration.
8. Start the run.
9. Download the default dataset or connect an integration.

If no search route is supplied through the API, the Actor uses the Madrid province search shown in the prefill.

### Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `searchUrls` | array | empty | Fotocasa URLs containing `combinedLocationIds` |
| `combinedLocationIds` | string array | Madrid when omitted | Nine numeric Fotocasa location components |
| `transactionTypeId` | integer | `1` | `1` for sale, `3` for rent |
| `filters` | object | `{}` | Price, rooms, bathrooms, surface, or type constraints |
| `maxItems` | integer | `20` | Maximum unique listings saved |
| `maxPagesPerSearch` | integer | `20` | Maximum API pages visited per search |
| `proxyConfiguration` | object | Residential, ES | Network route required by Fotocasa |

Use either `searchUrls` or `combinedLocationIds`, not both.
A URL without `combinedLocationIds` fails early instead of silently scraping a different area.

#### Supported filters

- `minPrice`
- `maxPrice`
- `minRooms`
- `maxRooms`
- `minBathrooms`
- `minSurface`
- `maxSurface`
- `propertyTypeId`

Filters are sent to Fotocasa where supported and are also checked on normalized rows.
Listings missing a field required by a filter are excluded.

### Input examples

#### Madrid homes for sale

```json
{
  "combinedLocationIds": ["724,0,28,173,0,0,0,0,0"],
  "transactionTypeId": 1,
  "maxItems": 25
}
```

#### Madrid rentals from a URL

```json
{
  "searchUrls": [{
    "url": "https://web.gw.fotocasa.es/v2/propertysearch/search?combinedLocationIds=724%2C0%2C28%2C173%2C0%2C0%2C0%2C0%2C0"
  }],
  "transactionTypeId": 3,
  "maxItems": 15
}
```

#### Filtered comparable properties

```json
{
  "combinedLocationIds": ["724,0,28,173,0,0,0,0,0"],
  "transactionTypeId": 1,
  "filters": {
    "minRooms": 2,
    "maxPrice": 750000,
    "minSurface": 50
  },
  "maxItems": 100
}
```

### Output example

A current Madrid search produces records shaped like this:

```json
{
  "listingId": 190344225,
  "listingUrl": "https://www.fotocasa.es/es/comprar/vivienda/madrid-capital/aire-acondicionado-calefaccion-trastero-ascensor-no-amueblado/190344225/d",
  "typeId": 2,
  "subtypeId": 1,
  "transactionTypeId": 1,
  "transaction": "sale",
  "price": 1700000,
  "currency": "EUR",
  "reducedPrice": false,
  "address": "Madrid Capital",
  "postalCode": "28013",
  "rooms": 3,
  "bathrooms": 3,
  "surfaceM2": 167,
  "advertiser": {
    "id": 9202776799392,
    "name": "Dils Lucas Fox",
    "typeId": 3,
    "phone": "+34933562989"
  },
  "imageUrls": ["https://static.fotocasa.es/images/ads/..."],
  "searchContext": {
    "combinedLocationIds": "724,0,28,173,0,0,0,0,0",
    "page": 1,
    "filters": {}
  },
  "scrapedAt": "2026-07-28T06:35:00.000Z"
}
```

Source records change continuously.
The example demonstrates the schema, not a guarantee that this listing remains active.

### How much does it cost to scrape Fotocasa property listings?

The Actor uses pay-per-event pricing:

- **Start:** $0.0015 per run.
- **Property listing:** $0.000041071 on the FREE tier.
- **Property listing:** $0.000035714 on the BRONZE tier.
- Higher plans receive the lower tier prices shown in Apify Console.

At the FREE-tier event rate:

| Listings | Event price estimate |
| ---: | ---: |
| 10 | about 0.00191 USD including start |
| 100 | about 0.00561 USD including start |
| 1,000 | about 0.04257 USD including start |

Actual billed event tier depends on your Apify plan.
The Actor charges only for normalized listings saved to the dataset, plus one start event.
Residential proxy and runtime costs are borne by the Actor developer under PPE.

### Scheduling and change monitoring

Run the Actor daily or weekly with the same location and filters.
Store each dataset ID with its run timestamp.
Compare records by `listingId` to identify:

- newly published inventory;
- removed inventory;
- price changes;
- advertiser activity;
- changes in available comparable properties.

Apify schedules can trigger the Actor automatically.
Webhooks can notify your data pipeline when a run succeeds or fails.

### Export and integration patterns

#### Spreadsheets and BI

Download CSV or Excel from the Dataset tab.
Use `price`, `surfaceM2`, and location levels for market summaries.

#### Databases and warehouses

Use `listingId` as the source key and `scrapedAt` as the observation time.
Keep historical observations when you need price-change analysis.

#### CRM review workflows

Send advertiser fields to a compliance-aware review queue.
Do not assume every public phone permits marketing contact.

#### GIS analysis

Use latitude and longitude only at the precision Fotocasa provides.
Coordinates can be approximate and must not be represented as exact private addresses.

### Use from the Apify API

Replace `YOUR_APIFY_TOKEN` with your token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~fotocasa-property-listings-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"combinedLocationIds":["724,0,28,173,0,0,0,0,0"],"transactionTypeId":1,"maxItems":25}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/fotocasa-property-listings-scraper').call({
  combinedLocationIds: ['724,0,28,173,0,0,0,0,0'],
  transactionTypeId: 1,
  maxItems: 25,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("automation-lab/fotocasa-property-listings-scraper").call(run_input={
    "combinedLocationIds": ["724,0,28,173,0,0,0,0,0"],
    "transactionTypeId": 1,
    "maxItems": 25,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with MCP and AI agents

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/fotocasa-property-listings-scraper"
```

#### Claude Desktop, Cursor, and VS Code setup

Use this equivalent JSON configuration in Claude Desktop, Cursor, or VS Code:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/fotocasa-property-listings-scraper"
    }
  }
}
```

Example prompts:

- “Collect 50 new Madrid Fotocasa homes for sale and summarize median price per square metre.”
- “Export Madrid rentals with at least two rooms to a CSV-ready dataset.”
- “Compare today’s Fotocasa IDs with yesterday’s dataset and list new inventory.”

### Tips for reliable runs

- Keep Spain Residential proxy enabled.
- Start with a small `maxItems` while validating a new location ID.
- Use a lower `maxPagesPerSearch` for scheduled recent-inventory snapshots.
- Split unrelated markets into separate runs for clearer failure and cost tracking.
- Treat null values as source omissions rather than extraction errors.
- Inspect logs when Fotocasa returns an unexpected response shape.

### Limitations

Fotocasa is an external website and can change its endpoint, fields, rate limits, or anti-bot controls.
The Actor retries transient request failures but does not bypass CAPTCHAs with human solvers.

Search results may not include every field shown on a detail page.
This Actor does not perform detail-page enrichment.
It does not download image binaries; it returns public image URLs.

`combinedLocationIds` is Fotocasa-specific.
SEO-only website URLs without that query parameter are rejected because they cannot be mapped safely without guessing.

A naturally empty source search succeeds with zero rows.
An invalid input or exhausted upstream request fails the run rather than returning a misleading empty dataset.

### Responsible use and legality

Use the Actor only for lawful purposes and data you are authorized to process.
Review Fotocasa's terms, robots guidance, and applicable Spanish and EU law.

Property descriptions, images, agency logos, and database content can be protected by intellectual-property or database rights.
Public availability does not automatically grant republication rights.

Public phone numbers and advertiser identities can be personal data.
Apply purpose limitation, retention controls, access controls, and opt-out processes where required.
Do not use the Actor for harassment, discriminatory housing decisions, or unlawful unsolicited marketing.

### Troubleshooting

#### Why did my URL fail before scraping?

The URL must belong to Fotocasa and include a `combinedLocationIds` query parameter.
Use the structured API URL or enter the location IDs directly.

#### Why are there no results?

Confirm the transaction type and location are active.
Then remove narrow filters and retry with a small limit.
A required filtered field that is missing from a listing causes that listing to be excluded.

#### Why did requests fail with 403 or a challenge?

Fotocasa blocks datacenter routes.
Restore the default Apify Residential proxy and Spain country selection.

#### Why is a field null?

Fotocasa does not expose every property field on every search result.
Null is preserved intentionally instead of guessing.

### Related Automation Labs Actors

For adjacent Spanish real-estate and location workflows, explore the [Automation Labs Actor catalog](https://apify.com/automation-lab).
No related Actor is hard-linked here until a verified same-buyer portfolio companion is available.

### FAQ

#### Does this Actor require a Fotocasa account?

No.
It reads anonymous public search data.

#### Does it support both sale and rent?

Yes.
Use transaction type `1` for sale and `3` for rent.

#### Can I scrape several locations in one run?

Yes.
Provide several `combinedLocationIds` values or several supported search URLs.
The global `maxItems` limit applies across them.

#### Does it scrape individual detail pages?

No.
The product intentionally uses the structured search route, which already exposes the documented fields.

#### How are duplicate listings handled?

Each stable Fotocasa listing ID is saved once per run, even if several searches return it.

#### Can I schedule it?

Yes.
Use Apify schedules and save each dataset for historical comparisons.

#### What happens when Fotocasa is temporarily unavailable?

The Actor makes bounded retries with backoff.
If recovery fails, the run fails clearly so monitoring can alert you.

# Actor input Schema

## `searchUrls` (type: `array`):

Fotocasa API or website search URLs containing the combinedLocationIds query parameter. Use this or Combined location IDs, not both.

## `combinedLocationIds` (type: `array`):

Fotocasa's nine-part numeric location identifier. Defaults to Madrid province when neither input route is supplied.

## `transactionTypeId` (type: `integer`):

Choose properties for sale or rent.

## `filters` (type: `object`):

Optional price, room, bathroom, surface, and Fotocasa type filters. Filters are enforced on every input route.

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

Stop after saving this many unique listings across all searches.

## `maxPagesPerSearch` (type: `integer`):

Safety bound for each search. One Fotocasa API page contains up to 30 listings.

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

Fotocasa blocks datacenter traffic. Keep the default Apify Residential proxy with Spain country targeting.

## Actor input object example

```json
{
  "searchUrls": [],
  "combinedLocationIds": [
    "724,0,28,173,0,0,0,0,0"
  ],
  "transactionTypeId": 1,
  "filters": {
    "minRooms": 2,
    "maxPrice": 500000
  },
  "maxItems": 20,
  "maxPagesPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Open the default dataset table with listing IDs, URLs, prices, property features, advertiser details, and extraction timestamps.

# 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 = {
    "searchUrls": [],
    "combinedLocationIds": [
        "724,0,28,173,0,0,0,0,0"
    ],
    "transactionTypeId": 1,
    "filters": {
        "minRooms": 2,
        "maxPrice": 500000
    },
    "maxItems": 20,
    "maxPagesPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "ES"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/fotocasa-property-listings-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 = {
    "searchUrls": [],
    "combinedLocationIds": ["724,0,28,173,0,0,0,0,0"],
    "transactionTypeId": 1,
    "filters": {
        "minRooms": 2,
        "maxPrice": 500000,
    },
    "maxItems": 20,
    "maxPagesPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "ES",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/fotocasa-property-listings-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 '{
  "searchUrls": [],
  "combinedLocationIds": [
    "724,0,28,173,0,0,0,0,0"
  ],
  "transactionTypeId": 1,
  "filters": {
    "minRooms": 2,
    "maxPrice": 500000
  },
  "maxItems": 20,
  "maxPagesPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}' |
apify call automation-lab/fotocasa-property-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/fotocasa-property-listings-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/a9b0Zp0aYCpMF07q2/builds/obx1smT14PZWR6S1B/openapi.json
