# Idealista Scraper (`datascrapers/idealista-scraper`) Actor

Scrape property listings from Idealista.com — search results and full detail pages with photos, features, location, and optional contact phones.

- **URL**: https://apify.com/datascrapers/idealista-scraper.md
- **Developed by:** [Farhan Ali](https://apify.com/datascrapers) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.20 / 1,000 listings

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?

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

**Idealista Scraper** creates a structured dataset of property listings collected from [Idealista.com](https://www.idealista.com), the real estate portal serving Spain, Italy, and Portugal. Each dataset item represents one property and can include price, size, bedrooms, photos, amenities, energy rating, agency details, and coordinates, with optional full detail pages and advertiser phone numbers. Query the source by Idealista search URL or location, filter by operation and property type, control the result limit with `maxItems` and `maxPages`, and retrieve records through the Apify Dataset API or export them as JSON, CSV, Excel, or other supported formats.

### Dataset at a glance

| Property | Value |
|---|---|
| Source | idealista.com (Spain, Italy, Portugal) |
| Record unit | One property listing, with optional nested detail and contact fields |
| Input methods | Idealista URL (`startUrls`) or location search (`searchQueries`) with operation and property type |
| Main identifiers | `propertyId`, listing `url` |
| Delivery | Apify Dataset and API |
| Export formats | JSON, CSV, Excel, XML, HTML (Apify dataset exports) |
| Update model | Fresh records per Actor run |
| Pricing | $2.50 per 1,000 listings; +$2.50 per 1,000 for details; +$2.50 per 1,000 for contact phones |

### Coverage and available records

The Actor collects properties from public Idealista pages using one of two entry points:

- **URL-based**: Pass Idealista search or listing URLs in `startUrls`. When set, location search and buy/rent filters are ignored.
- **Location-based**: Pass place names (for example `"Madrid"` or `"malaga teatinos"`) in `searchQueries`, combined with an `operation` (`buy` or `rent`) and a `propertyType`.

Supported property types: `homes`, `newHomes`, `rooms`, `holiday`, `garages`, `storageRooms`, `offices`, `commercial`, `transfers`, `land`, and `buildings`.

Record types and limits:

- **Listing-level records** are always collected: ID, URL, title, price, price per m², address, size, bedrooms, bathrooms, floor, thumbnail, and coordinates.
- **Detail-page fields** are conditional: photos, amenities, energy rating, agency name and URL, and full description are returned when `scrapeListingDetails` is enabled.
- **Contact fields** are conditional: advertiser phone numbers are returned only when `includeContactPhones` is enabled.
- **Result cap**: `maxItems` limits the number of listings collected (`0` means unlimited); `maxPages` limits search-result pages scanned per search URL.

Known exclusions: rooms and holiday stays are rent-only, and new homes and transfers are buy-only (Idealista's own category rules). Phone numbers depend on a protected endpoint and may be absent even when enabled. Each run captures page state at run time; no historical snapshots are stored.

### Data dictionary

Field names below match dataset record JSON properties exactly. Fields marked *conditional* appear only when the corresponding input toggle is enabled.

| Field | Type | Nullable | Description | Example |
|---|---:|---|---|---|
| `propertyId` | string | No | Idealista listing identifier; best stable deduplication key | `110788250` |
| `url` | string | No | Canonical listing URL | `https://www.idealista.com/inmueble/110788250/` |
| `title` | string | No | Listing title | `Piso en Calle Poetisa Alfonsina Domínguez, 3, El Consul - El Romeral, Málaga` |
| `price` | number | Yes | Asking price in EUR | `469000.0` |
| `pricePerSqm` | number | Yes | Price per m² in EUR | `4303.0` |
| `address` | string | Yes | Street address | `Piso en venta en Calle Poetisa Alfonsina Domínguez, 3` |
| `features` | string\[] | Yes | Search-card feature tags | `["Garaje incluido", "2 hab.", "109 m²"]` |
| `bedrooms` | number | Yes | Number of bedrooms | `2` |
| `bathrooms` | number | Yes | Number of bathrooms | `2` |
| `sqm` | number | Yes | Built area in m² | `109.0` |
| `floor` | string | Yes | Floor description | `6ª planta exterior con ascensor` |
| `thumbnail` | string | Yes | Main photo URL | `https://img4.idealista.com/...` |
| `images` | string\[] | Yes | Photo URLs (conditional — `scrapeListingDetails`) | `["https://img4.idealista.com/...", ...]` |
| `amenities` | string\[] | Yes | Detail-page amenity list (conditional — `scrapeListingDetails`) | `["109 m² construidos", "2 habitaciones", "Terraza"]` |
| `agency` | string | Yes | Advertiser agency name | `Norando Homes` |
| `agencyUrl` | string | Yes | Agency profile URL | `https://www.idealista.com/pro/norando-homes/` |
| `phoneNumbers` | array | Yes | Advertiser phone numbers (conditional — `includeContactPhones`) | `[]` |
| `latitude` | number | Yes | Map latitude | `36.7246144` |
| `longitude` | number | Yes | Map longitude | `-4.4862934` |
| `energyRating` | string | Yes | Energy certificate rating text | `Consumo: 1 kWh/m² año` |
| `searchUrl` | string | Yes | Source search URL that produced the record | `https://www.idealista.com/venta-viviendas/malaga/teatinos` |
| `scrapedAt` | string | Yes | Capture timestamp (ISO 8601, UTC) | `2026-08-19T14:00:26.019791+00:00` |

### Example dataset record

Real record produced with a location search (`searchQueries: ["malaga teatinos"]`), operation `buy`, property type `homes`, with listing details enabled.

```json
{
  "propertyId": "110788250",
  "url": "https://www.idealista.com/inmueble/110788250/",
  "title": "Piso en Calle Poetisa Alfonsina Domínguez, 3, El Consul - El Romeral, Málaga",
  "price": 469000.0,
  "pricePerSqm": 4303.0,
  "address": "Piso en venta en Calle Poetisa Alfonsina Domínguez, 3",
  "features": ["Garaje incluido", "2 hab.", "109 m²", "6ª planta exterior con ascensor"],
  "bedrooms": 2,
  "bathrooms": 2,
  "sqm": 109.0,
  "floor": "6ª planta exterior con ascensor",
  "thumbnail": "https://img4.idealista.com/blur/591_420_mq/0/id.pro.es.image.master/b3/e3/51/1455585508.jpg",
  "images": ["https://img4.idealista.com/blur/WEB_DETAIL_TOP-L-L/0/..."],
  "amenities": ["109 m² construidos", "2 habitaciones", "Terraza", "Piscina"],
  "agency": "Norando Homes",
  "agencyUrl": "https://www.idealista.com/pro/norando-homes/",
  "phoneNumbers": [],
  "latitude": 36.7246144,
  "longitude": -4.4862934,
  "energyRating": "Consumo: 1 kWh/m² año",
  "searchUrl": "https://www.idealista.com/venta-viviendas/malaga/teatinos",
  "scrapedAt": "2026-08-19T14:00:26.019791+00:00"
}
```

The record above was produced with this input:

```json
{
  "searchQueries": ["malaga teatinos"],
  "operation": "buy",
  "propertyType": "homes",
  "maxItems": 100,
  "maxPages": 5,
  "scrapeListingDetails": true,
  "includeContactPhones": false,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "ES" }
}
```

### Query and input reference

| Input | Type | Required | Default | Accepted values | Description |
|---|---|---|---|---|---|
| `startUrls` | array (requestListSources) | No\* | `[]` | Idealista search or listing URLs | URL-based entry point; overrides location search when set |
| `searchQueries` | array (stringList) | No\* | `["Madrid"]` | Location text (e.g. `"Madrid"`, `"malaga teatinos"`) | Location search used only when `startUrls` is empty |
| `operation` | string | No | `buy` | `buy`, `rent` | Buy or rent filter (location search only) |
| `propertyType` | string | No | `homes` | `homes`, `newHomes`, `rooms`, `holiday`, `garages`, `storageRooms`, `offices`, `commercial`, `transfers`, `land`, `buildings` | Listing category (location search only) |
| `maxItems` | integer | No | `0` | `0` (unlimited) or positive integer | Maximum listings to collect |
| `maxPages` | integer | No | `0` | `0` (unlimited) or positive integer | Maximum search pages per search URL |
| `scrapeListingDetails` | boolean | No | `true` | `true` / `false` | Fetch full detail pages (charged as listing details) |
| `includeContactPhones` | boolean | No | `false` | `true` / `false` | Fetch advertiser phone numbers (charged as contact details) |
| `requestDelayMs` | integer | No | `250` | `0` or positive integer | Delay between listing fetches in milliseconds |
| `locale` | string | No | `es` | `es`, `en`, `it`, `pt` | Site language for location suggestions |
| `proxyConfiguration` | object | No | Apify proxy, `RESIDENTIAL`, country `ES` | Apify proxy groups or custom proxies | Residential proxies are required; Spanish (ES) exits work best |

\* Provide either `startUrls` or `searchQueries` — at least one source is required.

Minimal request:

```json
{ "searchQueries": ["Madrid"], "operation": "buy", "propertyType": "homes" }
```

Advanced request (location search with details and contact phones):

```json
{
  "searchQueries": ["barcelona eixample"],
  "operation": "rent",
  "propertyType": "homes",
  "maxItems": 1000,
  "maxPages": 20,
  "scrapeListingDetails": true,
  "includeContactPhones": true,
  "requestDelayMs": 800,
  "locale": "es",
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "ES" }
}
```

### Retrieve the data through the API

The Actor runs on the Apify platform, so there is no server to host and no crawling infrastructure to maintain.

1. Start the Actor with a JSON input (console or API).
2. Wait for the run to finish, or use a synchronous endpoint if you want the response inline.
3. Retrieve items from the run's default dataset.
4. Paginate or export the dataset.

Python example:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR-APIFY-TOKEN")

run_input = {
    "searchQueries": ["malaga teatinos"],
    "operation": "buy",
    "propertyType": "homes",
    "maxItems": 100,
    "scrapeListingDetails": True,
}

run = client.actor("datascrapers/idealista-scraper").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["propertyId"], item["price"], item["pricePerSqm"])
```

Apify generates ready-to-run Python, JavaScript, and cURL examples on the Actor's API tab. Do not put a real API token in shared code or URLs.

### Data quality and record handling

- **Conditional fields**: images, amenities, and energy rating are present only when `scrapeListingDetails` is enabled; `phoneNumbers` is populated only when `includeContactPhones` is enabled and the contact endpoint succeeds.
- **Source changes**: Idealista page structure and values can change; unreadable fields are returned as null rather than fabricated.
- **Deduplication**: each run appends fresh records; the Actor does not deduplicate listings across runs. Use `propertyId` as the stable key and filter repeated runs against previously stored IDs.
- **Rate limits**: Idealista is protected by anti-bot controls and frequently challenges datacenter traffic. Residential proxies with Spanish exit nodes are required for consistent coverage; blocked requests are not charged.
- **Normalization**: prices are returned as numbers in EUR; `scrapedAt` is an ISO 8601 UTC timestamp. `phoneNumbers` is an empty array when no phone was retrieved.

### Export and pipeline examples

| Destination | Recommended method | Typical use |
|---|---|---|
| PostgreSQL / Supabase | Dataset API poll or webhook consumer | Store property inventory alongside price history |
| Google Sheets | Apify Google Sheets integration | Share market shortlists with agents |
| CRM (HubSpot, Salesforce) | Webhook on run completion | Push new listings into lead pipelines |
| S3 / cloud storage | Scheduled export via Apify scheduler + integration | Archival of market snapshots |

### Pricing and cost examples

The Actor uses pay-per-event pricing with three chargeable events, all billed per 1,000 events:

| Event | Trigger | Rate |
|---|---|---|
| Listing | Every listing record pushed to the dataset | $2.50 per 1,000 |
| Listing details | `scrapeListingDetails` enabled, detail page fetched | $2.50 per 1,000 |
| Contact details | `includeContactPhones` enabled, phone retrieved | $2.50 per 1,000 |

Example costs:

| Records | Configuration | Estimated base cost |
|---:|---|---:|
| 1,000 | Listing only | $2.50 |
| 10,000 | Listing only | $25.00 |
| 1,000 | Listing + details | $5.00 |
| 1,000 | Listing + details + contact phones | $7.50 |

Apify paid plans reduce the per-1,000 rate (for example $2.20 per 1,000 at the Gold tier). Failed or blocked extractions are not charged. Compute units consumed by the run are billed by your Apify plan. Estimates depend on the verified pricing model and the options selected for the run.

### Limitations and responsible data use

- The Actor collects publicly accessible data from Idealista pages only.
- Field availability depends on what Idealista renders at run time; some values can be null or missing, and site changes can alter fields.
- Phone numbers depend on a protected endpoint and may be absent even when enabled.
- The Actor does not provide historical snapshots unless you store them yourself.
- Large runs require residential proxies; without them, coverage may degrade due to anti-bot controls.
- You are responsible for compliance with Idealista's terms of service, applicable privacy law, and any contractual obligations before using the data.

### Dataset questions

#### What does one dataset item represent?

One property listing from Idealista. Detail-page fields (images, amenities, energy rating) are nested inside the same record, so a listing with full details still produces a single dataset item.

#### Which field should I use as a unique identifier?

`propertyId` is the stable Idealista listing identifier and is the recommended deduplication key. The `url` (canonical listing URL) is a reasonable secondary key.

#### Are fields nullable or conditional?

Yes. `images`, `amenities`, and `energyRating` exist only when `scrapeListingDetails` is enabled; `phoneNumbers` exists only when `includeContactPhones` is enabled. Within available records, fields that Idealista does not render (for example `floor` or `pricePerSqm`) are returned as null.

#### Can I retrieve the records as CSV or JSON?

Yes. The dataset can be exported as JSON, CSV, Excel, XML, or HTML from the Apify Console, and queried through the Dataset API.

#### Does the Actor return historical data?

No. Each run captures the state of the pages at run time. To track price changes over time, schedule repeated runs and store the outputs yourself.

#### What counts as a billable result?

Three pay-per-event charges apply: a listing charge for every record ($2.50 per 1,000), a listing-details charge for each detail page fetched ($2.50 per 1,000), and a contact-details charge for each phone retrieved ($2.50 per 1,000). A listing-only run incurs only the listing charge.

#### Why am I getting zero results or blocks?

Idealista is protected by anti-bot controls that challenge datacenter traffic. Keep the default residential proxy configuration with Spanish (ES) exit nodes, and increase `requestDelayMs` or reduce concurrency if you see repeated blocks in the run log.

### Related datasets from Data Scrapers

- **[Zoopla Scraper](https://apify.com/datascrapers/zoopla-scraper)** — UK property listings that can be compared with Idealista for cross-market pricing research.
- **[Apartments Scraper](https://apify.com/datascrapers/apartments-scraper)** — US rental listings useful alongside European property market analysis.
- **[Redfin Scraper](https://apify.com/datascrapers/redfin-scraper)** — US property listings and market signals for comparative real estate datasets.

### Data Scrapers support

Need an additional field, record type, or export workflow? Contact Data Scrapers at stardustspotlight@gmail.com. Include a sample source URL, required fields, expected record volume, and preferred delivery format.

# Actor input Schema

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

Optional Idealista search or listing URLs. When present, these are used and search location / buy-rent filters are ignored.

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

Location text such as "Madrid", "malaga teatinos", or "barcelona eixample". Used only when Start URLs are empty.

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

Operation used with search location. Ignored when Start URLs are provided. Rooms and holiday stays are rent-only; new homes and transfers are buy-only.

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

Listing category used with search location. Ignored when Start URLs are provided.

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

Maximum number of listings to scrape (0 = unlimited)

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

Maximum number of search result pages to scan per search URL (0 = unlimited)

## `scrapeListingDetails` (type: `boolean`):

Fetch the full detail page for each listing. Charged as listing-details when successful.

## `includeContactPhones` (type: `boolean`):

Fetch advertiser phone numbers. Charged as contact-details when successful.

## `requestDelayMs` (type: `integer`):

Delay between listing fetches in milliseconds

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

Site language used for location suggest (es, en, it, pt)

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

Residential proxies are required. Spanish (ES) exits work best for idealista.com.

## Actor input object example

```json
{
  "startUrls": [],
  "searchQueries": [
    "Madrid"
  ],
  "operation": "buy",
  "propertyType": "homes",
  "maxItems": 10,
  "maxPages": 0,
  "scrapeListingDetails": true,
  "includeContactPhones": false,
  "requestDelayMs": 250,
  "locale": "es",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}
```

# Actor output Schema

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

Dataset containing scraped Idealista listings

## `runStats` (type: `string`):

Record count and timestamps for this run

# 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 = {
    "searchQueries": [
        "Madrid"
    ],
    "maxItems": 10,
    "scrapeListingDetails": false,
    "includeContactPhones": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "ES"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascrapers/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 = {
    "searchQueries": ["Madrid"],
    "maxItems": 10,
    "scrapeListingDetails": False,
    "includeContactPhones": False,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "ES",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("datascrapers/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 '{
  "searchQueries": [
    "Madrid"
  ],
  "maxItems": 10,
  "scrapeListingDetails": false,
  "includeContactPhones": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}' |
apify call datascrapers/idealista-scraper --silent --output-dataset

```

## MCP server setup

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