# Idealista Scraper for Spain, Italy and Portugal (`fagenorn/idealista-scraper`) Actor

Scrape Idealista sale and rental listings in Spain, Italy and Portugal. Filter searches, export JSON or CSV, and add property details. Run summaries show where collection stopped.

- **URL**: https://apify.com/fagenorn/idealista-scraper.md
- **Developed by:** [anis](https://apify.com/fagenorn) (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.15 / 1,000 property listing saveds

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

Get sale and rental listings from Idealista in Spain, Italy and Portugal, with asking prices and locations. Use the results to compare properties or collect snapshots over time.

Search by a location URL, name or exact location ID. You can filter by price, bedrooms and publication date, then add descriptions, advertiser contacts, energy information and media where available. The actor covers homes, offices, commercial premises, garages and shared rooms. Shared-room searches use rent automatically.

This actor is independent and unaffiliated with Idealista.

### Workflows for property data teams

- **Compare asking prices.** Export listings from selected neighborhoods to CSV, then compare price, size and price per square meter in Excel or Google Sheets. Asking prices describe advertised properties; they are not completed sale prices.
- **Feed an application or analysis pipeline.** Fetch JSON through the Apify API and use `country` + `id` as the listing key. The timestamp and coverage summary help you check the age and scope of each snapshot.
- **Collect recurring snapshots.** Use an Apify schedule and store each run in your database. Compare matching listing IDs to track price changes; check coverage before treating an absent listing as removed.

### Idealista to n8n, CSV and Google Sheets

[Download the n8n workflow](https://api.apify.com/v2/key-value-stores/xLDTkVGSl8Yb7pZKz/records/idealista-n8n-workflow.json), or get the [automation kit with setup instructions and a Python exporter](https://api.apify.com/v2/key-value-stores/xLDTkVGSl8Yb7pZKz/records/idealista-automation-kit.zip). The template is free; actor runs use the rates in the Pricing tab.

1. Import the workflow into n8n. Create a Header Auth credential with `Authorization` as the name and `Bearer YOUR_APIFY_TOKEN` as its value; select it on the four HTTP Request nodes.
2. Edit **Configure search**. The preset collects up to 25 Madrid rental listings published in the last week, newest first, with a $0.02 maximum event charge.
3. Run manually and download the file from **Download CSV**. Each row includes the listing key, asking price, collection timestamp and coverage limits.
4. To append snapshots to Google Sheets, connect your credential, enter the document ID and enable the optional Sheets node. Enable the daily trigger after testing your destination. Both start disabled.

The workflow checks run status and coverage before exporting. It supports 1–1,000 listings per run and stops on failed or partial collection. Snapshots can contain the same listing across runs; change detection and alerts need a separate comparison step. See the [setup guide](https://api.apify.com/v2/key-value-stores/xLDTkVGSl8Yb7pZKz/records/README.md) or try the [recent Madrid rentals preset](https://apify.com/fagenorn/idealista-scraper/examples/collect-recent-madrid-rental-listings) directly in Apify.

For other automation platforms, use the [Apify Make integration](https://docs.apify.com/integrations/make).

### Getting started

1. Paste a location search URL into **Start URLs**. To search by name instead, clear that field, enter `Madrid`, `Milano` or `Lisboa` in **Locations by name**, and choose the country.
2. Set your filters and keep **Maximum listings** at 100 for the first run. Leave enrichment off if you only need prices and locations. Check the **Pricing** tab and set **Max cost per run**.
3. Use a residential proxy for cloud runs. The actor chooses the proxy country for a single-country search unless you set one yourself.
4. Open the dataset after the run. Check **Coverage and run summary** to see how much of the search was collected.

The prefilled URL searches Madrid homes for sale. If you add a location name while keeping that URL, the actor searches both. Location names resolve to the first Idealista autocomplete suggestion. Check the resolved location in the summary; an exact location ID is useful when a name could refer to several areas.

```json
{
  "startUrls": [{ "url": "https://www.idealista.com/en/homes-for-sale-in-madrid/" }],
  "priceFrom": 100000,
  "priceTo": 300000,
  "bedrooms": ["2", "3"],
  "maxItems": 100,
  "includeDetails": false,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

### Search options

| Option | Behavior |
|---|---|
| Start URLs | Search locations on `idealista.com`, `.it` or `.pt`. The domain and path set the country, sale or rent, and property type. Examples: `/alquiler-viviendas/madrid-madrid/`, `/venta-viviendas-chamberi-madrid/`, `/affitto-case/milano-milano/`, `/comprar-casas/lisboa/`. Individual property URLs and map or polygon searches are unsupported. |
| Locations by name | Select the country and operation in the form. The actor resolves the name through autocomplete. |
| Location IDs | IDs such as `0-EU-ES-28-07-001-079` identify an area and its country. You can copy `location.locationId` from an earlier result. |
| Price from / to | Prices are in euros, or euros per month for rentals. Zero leaves that bound unset. |
| Bedrooms | Select exact counts; a listing can match any selected count. `0` means studio; `4+` is unsupported. |
| Published within | Yesterday, last week or last month, as reported by Idealista. Choose Publication date / Descending to get the newest first. |
| Enrich with details | Adds one request per listing and merges the available details into the row. If the request fails, the actor keeps the base listing and does not charge for enrichment. |
| Maximum listings | Caps output across all searches. The default is 100; zero removes the cap. Use Apify's run-cost setting to limit event charges. |
| Max listings per query | Defaults to 1,000 for each location and filter combination. |
| Max pages per query | Zero calculates a page limit from the per-query item limit. A positive number sets a page cap. |
| Filters JSON | Extra flat Idealista filters, e.g. `{"minSize":80,"bathrooms":"2,3","terrace":true}`. Idealista may ignore filter names it does not recognize. |
| Request settings | Defaults to concurrency 8 and 750 ms between requests per identity. Without a proxy, concurrency is at most 8 and the interval is at least 1,200 ms. |

Spanish URL price tokens `precio-desde_…` and `precio-hasta_…` become search filters. Other URL filters and unsupported query parameters produce an error explaining which input fields to use. Non-zero price fields, selected Bedrooms and a specific Published within value override the matching Filters JSON keys and URL prices. Leaving these fields at their defaults keeps your advanced filters.

### Results and exports

Each dataset row is a property listing. The actor removes duplicates within a run using `country` + `id`. When searches overlap, a listing appears once: each query's `items` count tells you how many rows it added, not how many listings exist in that market. The dataset has three views: Listings, Price and location, and Enrichment and contacts.

The Output tab has links to the full JSON dataset, an overview CSV and the `OUTPUT` summary in the key-value store. JSON includes every saved field. The overview CSV contains the columns in that view; you can select other views in the dataset UI.

Example base listing, with optional fields supplied by Idealista:

```json
{
  "id": "123456789",
  "title": "Example apartment in Madrid",
  "url": "https://www.idealista.com/inmueble/123456789/",
  "price": { "amount": 250000, "currency": "€", "perSqm": 3125 },
  "sizeM2": 80,
  "rooms": 2,
  "bathrooms": 1,
  "location": { "municipality": "Madrid", "latitude": 40.42, "longitude": -3.70 },
  "country": "es",
  "operation": "sale",
  "searchLocation": "Madrid",
  "scrapedAt": "2026-10-08T12:00:00.000Z",
  "detailStatus": "not-requested"
}
```

Successful enrichment sets `detailStatus` to `succeeded`. It can add `contact`, `characteristics`, `energyCertification`, fuller descriptions, media, `flags` and `dates`. A failed request sets the status to `failed` and adds `detailError`.

Some listings have no contact details, energy rating or exact address. Coordinates can be approximate. Price reductions appear in `price.dropAmount`, `price.dropPercentage` and `price.dropDate` only when Idealista supplies them. `scrapedAt` records when the actor collected the listing; `dates.modified` is Idealista's modification timestamp in milliseconds since the Unix epoch.

For API integrations, read `defaultDatasetId` and `defaultKeyValueStoreId` from the completed run:

```text
GET https://api.apify.com/v2/datasets/{defaultDatasetId}/items?format=json
GET https://api.apify.com/v2/key-value-stores/{defaultKeyValueStoreId}/records/OUTPUT
```

Use an Authorization header for private resources. Paginate large downloads with `offset` and `limit`. See the [dataset items API](https://docs.apify.com/api/v2/dataset-items-get).

### Coverage and limits

Idealista limits how far you can page through a search. The observed window is around 2,500 to 3,000 listings per query, which broad city searches can exceed. The actor does not split those markets automatically. Search smaller districts or separate price bands, then remove duplicates from the combined exports.

The `OUTPUT` summary contains row and detail counts, detail failures, charge counts, request failures and timing. It also reports coverage for each country, location, sale or rent, property type and filter combination. Each coverage entry has Idealista's `total`, the query's contributed `items`, processed and advertised page counts, `complete` and `reason`.

| Reason | Meaning |
|---|---|
| `complete` | Every advertised page was fetched or restored from a checkpoint. Listings can still change during pagination. |
| `depth-window` | Idealista returned a different page from the one requested. The actor discards it and stops queued requests for that query. Requests already in progress can finish. |
| `page-cap` | The configured or calculated page cap stopped collection. |
| `max-items` / `query-limit` | The global or per-query listing cap stopped collection. |
| `budget-limit` | The actor limited collection to rows whose combined listing and successful-enrichment charges fit the remaining budget. Failed enrichment can leave some budget unused. |
| `failed` | A location lookup or page request failed. Check `failures`; any rows already saved remain available. |

The summary status is `succeeded` when collection finishes, `limited` when a cap stops it, `partial` when it returns data with request or enrichment failures, and `failed` when collection fails. Check coverage even after a successful Apify run: the dataset may contain only part of a market. A failed run can also have saved rows.

After a platform migration, checkpoints restore saved progress if the normalized input is identical. An abrupt crash can leave duplicate rows, so deduplicate exported snapshots by country and ID.

### Scheduled runs

Use Published within and Publication date / Descending when scheduling snapshots. Adjust the page and item limits until coverage is complete; one or two pages only samples an active market. Each run creates a separate dataset. Save `country` + `id` in your own database to find new listings and compare prices over time. Change detection, alerts and deduplication across runs belong in that workflow.

### Troubleshooting

Enrichment adds a request for each listing. Run time depends on the search size, proxies and Idealista's responses.

If the dataset is empty, check the failure messages, resolved location and proxy configuration. A search with no matching listings succeeds with zero rows. A run whose requests are all blocked fails.

For help, open an issue on the actor's Apify page with the run ID and input, after removing any credentials or private proxy URLs. Changes to Idealista's API may require an actor update.

# Changelog

This Actor's version history is a separate document: https://apify.com/fagenorn/idealista-scraper/changelog.md

# Actor input Schema

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

Paste an Idealista location search URL from Spain, Italy or Portugal. The domain, operation and property type are detected; pagination is automatic. Spanish precio-desde/precio-hasta URL filters are supported. Other URL filters are rejected with instructions to use the fields below, so your search is never broadened silently. The default run returns up to 100 listings.

## `searchQueries` (type: `array`):

Alternative to URLs: enter location names such as Madrid, Lisboa or Milano and select the correct country. Leave empty when using only URLs. Multiple input sources combine.

## `locationIds` (type: `array`):

Alternative for exact targeting: Idealista location IDs, such as 0-EU-ES-28-07-001-079. Copy location.locationId from a listing in a previous run. The country is detected from the ID.

## `country` (type: `string`):

idealista market (derived automatically from Start URL domains).

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

Buy (sale) or rent. Ignored for Start URLs that already encode the operation.

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

homes = flats/houses, premises = commercial spaces, bedrooms = shared rooms (flatshare — rent only; sale queries are switched to rent automatically). Ignored for Start URLs that already encode the type.

## `locale` (type: `string`):

Language for descriptions and texts returned by idealista.

## `priceFrom` (type: `integer`):

Minimum price. For rent queries this is per month. 0 = no lower bound.

## `priceTo` (type: `integer`):

Maximum price. For rent queries this is per month. 0 = no upper bound.

## `bedrooms` (type: `array`):

Exact bedroom counts to include — list several for any-of (e.g. 2 and 3 matches both 2- and 3-bedroom homes). 0 = studio. Leave empty for any number of bedrooms.

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

Only listings idealista published in this window (yesterday / last week / last month). Pair with Order by = publicationDate for monitoring runs on a schedule.

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

Adds descriptions, contact information, energy information and media when Idealista supplies them. Successful enrichment has an additional event charge; check the Pricing tab for current rates. Failed detail requests keep the base listing without an enrichment charge.

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

Maximum listings saved across all queries. Start with 100 to check your search. Set 0 for no global listing cap. Use Apify Max cost per run to limit monetary charges; proxy and other platform costs follow the pricing settings.

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

Maximum saved listings for each location query (default 1,000). Idealista also has an upstream result window around 2,500–3,000 per query. Check coverage and split large markets by district or price range.

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

Hard cap on pages per query (0 = derive from max listings per query). 1 = first page only.

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

Extra Idealista filters, for example {"minSize":80,"bathrooms":"2,3","terrace":true}. Explicit non-zero price fields, a non-empty Bedrooms list, and a specific Published within value override matching JSON keys. They also override imported URL price filters. Default 0/any/empty values do not erase JSON filters. Unsupported upstream filter names may be ignored by Idealista.

## `sort` (type: `string`):

Direction of the selected order: descending gives newest first with Publication date; ascending gives oldest first. Relevance is Idealista’s own ranking.

## `order` (type: `string`):

weigh = idealista's relevance order, publicationDate = newest first (use for monitoring).

## `maxConcurrency` (type: `integer`):

Maximum parallel requests, each with a device and proxy session. Start with the default of 30; actual speed depends on proxies, upstream responses and retries. Without a proxy this is capped at 8.

## `minRequestIntervalMs` (type: `integer`):

Politeness gap between two requests from the same simulated device. Lower = faster.

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

Residential proxy recommended for Apify cloud runs. For a single-country run, the proxy country is derived automatically unless explicitly supplied. Mixed-country runs leave the proxy country unset. Without a proxy, Idealista may block the cloud IP. The configured Apify proxy is included in the launch event prices.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.idealista.com/en/homes-for-sale-in-madrid/"
    }
  ],
  "country": "es",
  "operation": "sale",
  "propertyType": "homes",
  "locale": "en",
  "priceFrom": 0,
  "priceTo": 0,
  "publishedWithin": "any",
  "includeDetails": false,
  "maxItems": 100,
  "maxItemsPerQuery": 1000,
  "maxPages": 0,
  "filters": {},
  "sort": "desc",
  "order": "weigh",
  "maxConcurrency": 8,
  "minRequestIntervalMs": 750,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `listings` (type: `string`):

Full JSON results, including every available enrichment field.

## `csv` (type: `string`):

Download a flat CSV with the overview columns.

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

Counts, failures, charges and complete/partial coverage for every query.

# 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.idealista.com/en/homes-for-sale-in-madrid/"
        }
    ],
    "filters": {},
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fagenorn/idealista-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.idealista.com/en/homes-for-sale-in-madrid/" }],
    "filters": {},
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fagenorn/idealista-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.idealista.com/en/homes-for-sale-in-madrid/"
    }
  ],
  "filters": {},
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call fagenorn/idealista-scraper --silent --output-dataset

```

## MCP server setup

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