# Idealista Commercial Scraper — Offices, Naves & Terrenos (`studio-amba/idealista-commercial-scraper`) Actor

Scrape commercial real estate and land listings from Idealista in Spain (idealista.com), Italy (idealista.it), and Portugal (idealista.pt). Offices, retail premises, industrial units, and terrenos with prices, EUR/m2 rates, surfaces, and addresses for sale or rent.

- **URL**: https://apify.com/studio-amba/idealista-commercial-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 result scrapeds

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/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

## Idealista Commercial Scraper

Extract commercial property and land listings from Idealista, the leading property portal in Southern Europe. One actor covers the commercial categories of idealista.com (Spain), idealista.it (Italy), and idealista.pt (Portugal): offices, retail premises, industrial units, and land.

### What does this actor do?

You pick a market (Spain, Italy, or Portugal), a segment (office, retail, industrial, or land), sale or rent, and a city. The actor returns structured JSON for every listing in that search: headline, address, price with its unit, the EUR/m2 rate where Idealista quotes one, surface, land zoning for plots, photos, and the listing URL.

It scrapes the same categories a manual visitor sees:

- Spain: oficinas, locales, naves industriales, terrenos
- Italy: uffici, negozi, capannoni, terreni
- Portugal: escritórios, espaços comerciais, armazéns, terrenos

### Why use it?

- **Market research.** Track office rents per m2 across Madrid, Milano, and Lisboa from one dataset with consistent fields.
- **Site sourcing.** Pull every industrial unit or buildable plot in a city into your own pipeline instead of paging through results by hand.
- **Investment screening.** Sale listings in the retail category include units sold with sitting tenants ("en rentabilidad"), and land listings carry their zoning classification.
- **Cross-border coverage.** The record shape is identical for all three countries, so a Spain + Italy + Portugal comparison needs no field mapping.

Commercial listings quote prices in several ways. This actor keeps that intact: the parsed number, the original display string, and the unit are separate fields. Price-on-application listings get a null price instead of a made-up number.

### How to scrape Idealista commercial data

1. Create a free Apify account and open this actor.
2. Choose the market: `es` (idealista.com), `it` (idealista.it), or `pt` (idealista.pt).
3. Choose the segment: `office`, `retail`, `industrial`, or `land`.
4. Choose `sale` or `rent` and enter a city, in the local spelling or English (`Madrid`, `Valencia`, `Milano`, `Lisboa`, `Porto`).
5. Optionally set price and size bounds.
6. Run it. Results land in the dataset as JSON, downloadable as CSV, Excel, or through the API.

The actor handles the cookie-consent wall and Idealista's anti-bot protection through a scraping browser with country-matched residential exits. You do not need to configure a proxy.

### Input options

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `market` | string | `es` | `es` (idealista.com), `it` (idealista.it), or `pt` (idealista.pt). |
| `segment` | string | `office` | `office`, `retail`, `industrial`, or `land`. |
| `listingType` | string | `sale` | `sale` or `rent`. |
| `searchQuery` | string | market capital | City or area, e.g. `Madrid`, `Valencia`, `Sevilla`, `Milano`, `Torino`, `Lisboa`, `Porto`. |
| `minPrice` / `maxPrice` | integer | (none) | Price bounds in EUR. Total for sale, per month for rent. |
| `minSize` / `maxSize` | integer | (none) | Surface bounds in m2. |
| `maxResults` | integer | `100` | Hard cap on returned listings (1 to 1,800). Idealista serves at most ~60 result pages per search, so narrow the location or add filters for full coverage of a large market. |

#### Example input

```json
{
    "market": "es",
    "segment": "office",
    "listingType": "rent",
    "searchQuery": "Madrid",
    "maxResults": 100
}
```

Land plots for sale around València:

```json
{
    "market": "es",
    "segment": "land",
    "listingType": "sale",
    "searchQuery": "Valencia",
    "maxResults": 50
}
```

Warehouses in Lisboa:

```json
{
    "market": "pt",
    "segment": "industrial",
    "listingType": "rent",
    "searchQuery": "Lisboa",
    "maxResults": 50
}
```

### A note on retail and industrial

Idealista groups retail premises and industrial units into one category per market ("Locales o naves" in Spain, "Locali o capannoni" in Italy, "Espaços comerciais ou armazéns" in Portugal). The actor reads the typology from each listing and returns only the type you asked for. The `propertyType` field carries that per-listing classification, so a `retail` run tells you exactly what each record is. An `industrial` run scans more result pages than other segments to fill your `maxResults`, because industrial units are the minority type in the shared category.

### Output fields

| Field | Type | Example |
|-------|------|---------|
| `title` | string | `"Oficina en Calle de Almagro, 21, Almagro, Madrid"` |
| `segment` | string | `"office"`, `"retail"`, `"industrial"`, `"land"` |
| `propertyType` | string | Per-listing typology: `"office"`, `"retail"`, `"industrial"`, `"land"`, `"building"`, `"other"` |
| `listingType` | string | `"sale"` or `"rent"` |
| `price` | number or null | `24000` (null when price on application) |
| `currency` | string | `"EUR"` (always) |
| `priceUnit` | string or null | `"EUR"` for sale totals, `"EUR/month"` for rents |
| `priceText` | string or null | `"24.000€/mes"`, `"305.000€"`, `"4.200€/mês"` |
| `pricePerM2` | number or null | `40` (for rents: EUR per m2 per month) |
| `pricePerM2Text` | string or null | `"40,00 €/m²"` |
| `surface` | number or null | `600` (m2) |
| `landClassification` | string or null | `"Urbanizable"`, `"Edificabile"`, `"Urbano"` (land listings) |
| `detailsText` | array | All detail chips verbatim, e.g. `["600 m²", "40,00 €/m²"]` |
| `address` | string or null | `"Calle de Almagro, 21, Almagro, Madrid"` |
| `description` | string or null | Listing description snippet |
| `imageUrl` | string or null | First photo URL |
| `imageCount` | number or null | Number of photos |
| `agencyName` | string or null | Advertising agency, when branded |
| `propertyCode` | string | `"99679609"` (Idealista listing ID) |
| `market` / `country` | string | `"es"` / `"ES"` |
| `source` | string | `"www.idealista.com"` |
| `url` | string | Canonical listing URL |
| `scrapedAt` | string | ISO timestamp |

#### Example output

```json
{
    "title": "Oficina en Calle de Almagro, 21, Almagro, Madrid",
    "segment": "office",
    "propertyType": "office",
    "listingType": "rent",
    "price": 24000,
    "currency": "EUR",
    "priceUnit": "EUR/month",
    "priceText": "24.000€/mes",
    "pricePerM2": 40,
    "pricePerM2Text": "40,00 €/m²",
    "surface": 600,
    "landClassification": null,
    "address": "Calle de Almagro, 21, Almagro, Madrid",
    "propertyCode": "99679609",
    "market": "es",
    "country": "ES",
    "source": "www.idealista.com",
    "url": "https://www.idealista.com/inmueble/99679609/",
    "scrapedAt": "2026-08-18T14:00:00.000Z"
}
```

### Cost per result

The actor charges per dataset item plus a small run-start fee. A run pays for the anti-bot browser infrastructure needed to reach Idealista reliably; expect a typical 100-listing run to finish in a few minutes. A run's usage cost only settles after the run reports SUCCEEDED, so read the final cost after the run finishes rather than mid-run.

### Limits and behavior worth knowing

- Idealista caps any single search at roughly 60 result pages (~1,800 listings). For complete coverage of a large market, run per district or with price/size bands.
- `price` is null for price-on-application listings. That is normal in this vertical, and it is why the price assertions in your own pipeline should tolerate nulls.
- For rent listings the per-m2 rate Idealista quotes is per month.
- Sale prices are totals; the land category also shows zoning (`landClassification`) straight from the card.
- If a location is not recognized, the actor falls back to the market's capital rather than returning nothing, and says so in the log.

### Related actors

- [Idealista Scraper](https://apify.com/studio-amba/idealista-scraper) — residential listings (pisos, casas) on the same three markets, with detail-page enrichment and an FSBO filter.

### FAQ

**Which countries does this cover?**
Spain, Italy, and Portugal — the three markets Idealista operates.

**Does it scrape listing detail pages?**
No. It reads search result cards, which carry the commercially relevant fields (price, rate per m2, surface, zoning, address). That keeps runs fast and the per-result cost low.

**Can I get both retail and industrial in one run?**
Run `segment=retail` and `segment=industrial` separately, or use `retail` and read the `propertyType` field — the shared category is classified per listing.

**Is scraping Idealista legal?**
The actor collects publicly visible listing data only. You are responsible for using the data in compliance with applicable law and Idealista's terms.

# Actor input Schema

## `market` (type: `string`):

Which Idealista site to scrape.

## `segment` (type: `string`):

Commercial segment: offices (oficinas/uffici/escritórios), retail premises (locales/negozi/lojas), industrial units (naves/capannoni/armazéns), or land (terrenos/terreni). Retail and industrial share one Idealista category — the actor classifies each listing and returns only the requested type.

## `listingType` (type: `string`):

Buy (sale) or rent.

## `searchQuery` (type: `string`):

City or area to search (e.g. 'Madrid', 'Valencia', 'Milano', 'Lisboa'). Leave empty for the market's capital (Madrid / Roma / Lisboa).

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

Minimum price in EUR (total for sale, per month for rent).

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

Maximum price in EUR (total for sale, per month for rent).

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

Minimum surface in square meters.

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

Maximum surface in square meters.

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

Hard cap on listings returned. Idealista serves at most ~60 pages (~1,800 listings) per search — narrow the location or add price/size filters for full coverage of a large market.

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

Kept for compatibility. Transport runs through a scraping browser that manages its own country-matched residential exits — this setting is not used.

## Actor input object example

```json
{
  "market": "es",
  "segment": "office",
  "listingType": "rent",
  "searchQuery": "Madrid",
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}
```

# 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 = {
    "market": "es",
    "segment": "office",
    "listingType": "rent",
    "searchQuery": "Madrid",
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "ES"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/idealista-commercial-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 = {
    "market": "es",
    "segment": "office",
    "listingType": "rent",
    "searchQuery": "Madrid",
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "ES",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/idealista-commercial-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 '{
  "market": "es",
  "segment": "office",
  "listingType": "rent",
  "searchQuery": "Madrid",
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  }
}' |
apify call studio-amba/idealista-commercial-scraper --silent --output-dataset

```

## MCP server setup

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