# Immobiliare.it Scraper — Italian Property Listings & Prices (`tortuga/immobiliare-scraper`) Actor

Scrape Immobiliare.it homes for sale and rent by town, zone, province, search URL or agency: price, €/m², price drops, size, rooms, address, coordinates, photos, agency, full details.

- **URL**: https://apify.com/tortuga/immobiliare-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (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.70 / 1,000 listing 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/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

## Immobiliare.it Scraper — Italian Property Listings & Prices

Scrape property listings from **Immobiliare.it**, Italy's largest property portal, for sale and to rent: price, price drop, price per m², size, rooms, bedrooms, bathrooms, floor, lift and other features, address, zone and coordinates, condition, heating, every photo and floor plan, the agency, and the publication and update dates. Turn on details for the full description, all characteristics, year built, energy class, condominium fees, availability, virtual tour, agent and agency page.

Paste any Immobiliare.it search URL with your filters, a listing URL or an agency page, or just type a town, province or zone and set type, price, size, rooms and sort in the input. No login, no browser, no expensive proxy: one request returns 20 listings and every page of a search is reachable. Export to JSON, CSV or Excel, or use the API. You pay per listing you get.

### What data does Immobiliare.it Scraper extract?

Every listing from a search (the `result` event):

| Field | Description |
|---|---|
| `id`, `url` | Immobiliare.it listing id and listing URL (`https://www.immobiliare.it/annunci/133212744/`) |
| `title`, `propertyType`, `category` | Type, street, zone and town (`Appartamento Via Bernardino Verro, Vigentino - Fatima, Milano`); property type (`Appartamento`, `Villa`, `Attico`, `Progetto`...) and category (`Residenziale`, `Nuove costruzioni`, `Uffici`...) |
| `operation`, `isAuction` | `sale` or `rent`; `true` for court auctions (aste) |
| `price`, `currency`, `priceText`, `priceOnRequest` | Price in EUR (monthly rent for rentals), as displayed (`€ 1.600/mese`, `da € 160.000`), and whether the price is hidden ("prezzo su richiesta") |
| `originalPrice`, `priceDropPercent` | Price before the reduction and the drop in % when Immobiliare.it shows one |
| `pricePerSqm` | Price per m² (the site's own figure with details, otherwise price / size) |
| `size`, `rooms`, `roomsText`, `bedrooms`, `bathrooms`, `floor` | m², rooms (locali; `roomsText` keeps `5+` or a range like `1-4` for projects), bedrooms, bathrooms, floor as shown (`T` = ground floor, `R` = raised) |
| `hasLift`, `hasBalcony`, `hasTerrace`, `hasGarden`, `hasCellar`, `isFurnished`, `hasPool`, `hasGarage` | Features; `null` means not stated (details fill most of them) |
| `condition`, `heating`, `features`, `isLuxury` | `Ottimo / Ristrutturato`, `Autonomo`, the listing's feature tags (`fibra ottica`, `porta blindata`, `esposizione doppia`...), luxury flag |
| `region`, `province`, `provinceCode`, `city`, `cityId`, `zone`, `zoneId`, `subZone`, `subZoneId`, `address`, `postalCode` | Location as published (zone = Immobiliare.it macrozone, e.g. `Navigli`; sub-zone = microzone) |
| `latitude`, `longitude`, `locationExact` | Map point; `locationExact: false` means the point is approximate |
| `photoCount`, `photos`, `floorPlans` | All photos (large size) and floor plan images |
| `isNewConstruction`, `unitsCount`, `isNewListing`, `highlight` | New-build project and its number of units, "new" badge, paid visibility tier (`Sky`, `Top`, `Premium`, `Vetrina`, `Star`) |
| `isPrivateSeller`, `advertiserType`, `agencyName`, `agencyId`, `agencyLogo` | Private seller or agency / builder and its name, id and logo |
| `publishedDate`, `updatedDate` | When the listing was created and last updated (ISO, UTC) |
| `sourceUrl`, `scrapedAt` | The search (URL or location) it came from and when it was scraped |

With **Include listing details** on, or for listing URLs (the extra `detail` event), each listing also gets:

| Field | Description |
|---|---|
| `description`, `headline`, `reference` | Full description, the advertiser's headline and reference code |
| `characteristics` | The whole characteristics table as label → value (`Tipologia`, `Contratto`, `Disponibilità`, `Piano`, `Cucina`, `Box, posti auto`, `Anno di costruzione`, `Riscaldamento`, `Climatizzazione`...) |
| `yearBuilt`, `availability`, `kitchen`, `buildingFloors`, `concierge`, `airConditioning` | The most used rows as their own fields |
| `energyClass`, `energyConsumption`, `energyCertification` | Energy class (`A4` ... `G`), consumption (`≥ 175 kWh/m² anno`), or the certification status when there is no class yet |
| `condominiumFees`, `costs` | Monthly condominium fees in EUR and every published cost line (heating costs, deposit...) |
| `hasVirtualTour`, `virtualTourUrl`, `videoUrls` | Virtual tour and video links |
| `agentName`, `agencyUrl`, `agencyAddress` | The agency's contact agent as shown on the listing, its Immobiliare.it page and office address |
| `units`, `constructionProgress` | For new-build projects: each unit (type, m², rooms, floor, price) and the works status |
| `detailError` | Why details are missing for this listing (e.g. removed), otherwise `null` |

Phone numbers are behind a "call" button on the site and are never collected. For private sellers the actor outputs no name, no street address, and a map point rounded to about 1 km.

### How to scrape Immobiliare.it listings

1. Either paste **Start URLs**: run your search on immobiliare.it (any town, zone or province, sale or rent, any property type, with price, size, rooms and sort set) and copy the address bar, e.g. `https://www.immobiliare.it/vendita-case/milano/?prezzoMassimo=400000&criterio=dataModifica&ordine=desc`. Listing URLs (`/annunci/ID/`) and agency pages (`/agenzie-immobiliari/ID/...`) work too.
2. Or type **Locations**: `Milano`, `Roma`, `provincia di Firenze`, `Navigli, Milano`, `Trastevere, Roma`. Pick **For sale or to rent**, **Property type** and optionally **Min / Max price**, **Min / Max size**, **Min / Max rooms** and **Sort by**.
3. Set **Max items** to cap the run and the cost.
4. Turn on **Include listing details** only if you need the fields from the second table.
5. Click **Start** and download the dataset as JSON, CSV or Excel, or read it with the API.

#### Input example

```json
{
  "locations": ["Milano", "Navigli, Milano"],
  "operation": "sale",
  "propertyType": "apartments",
  "minPrice": 200000,
  "maxPrice": 500000,
  "minSize": 60,
  "minRooms": 2,
  "sort": "newest",
  "maxItems": 200,
  "includeDetails": false
}
```

The same with a URL: `{"startUrls": [{"url": "https://www.immobiliare.it/vendita-appartamenti/milano/?prezzoMinimo=200000&prezzoMassimo=500000&superficieMinima=60&localiMinimo=2&criterio=dataModifica&ordine=desc"}]}`.

#### Output example

One listing with details (long arrays shortened):

```json
{
  "id": "133212744",
  "url": "https://www.immobiliare.it/annunci/133212744/",
  "title": "Appartamento Via Bernardino Verro, Vigentino - Fatima, Milano",
  "propertyType": "Appartamento",
  "category": "Residenziale",
  "operation": "sale",
  "isAuction": false,
  "region": "Lombardia",
  "province": "Milano",
  "city": "Milano",
  "zone": "Ripamonti, Vigentino",
  "subZone": "Vigentino - Fatima",
  "address": "Via Bernardino Verro",
  "postalCode": "20141",
  "latitude": 45.4349,
  "longitude": 9.196,
  "locationExact": true,
  "price": 598000,
  "currency": "EUR",
  "priceText": "€ 598.000",
  "originalPrice": null,
  "priceDropPercent": null,
  "pricePerSqm": 5339,
  "size": 112,
  "rooms": 3,
  "bedrooms": 2,
  "bathrooms": 1,
  "floor": "1",
  "hasLift": true,
  "hasBalcony": false,
  "hasTerrace": false,
  "hasGarden": true,
  "hasGarage": true,
  "isFurnished": true,
  "condition": "Ottimo / Ristrutturato",
  "heating": "Centralizzato, a radiatori",
  "features": ["Armadio a muro", "Cancello elettrico", "Esposizione doppia", "Fibra ottica", "Piscina", "..."],
  "highlight": "Sky",
  "isPrivateSeller": false,
  "advertiserType": "agency",
  "agencyName": "Gastone Di Paola Immobiliare",
  "agencyId": 51157,
  "agencyLogo": "https://pic.im-cdn.it/imagenoresize/301705097.jpg",
  "agencyUrl": "https://www.immobiliare.it/agenzie-immobiliari/51157/gastone-di-paola-milano/",
  "photoCount": 36,
  "photos": ["https://pwm.im-cdn.it/image/2021584926/xxl.jpg", "..."],
  "floorPlans": ["https://pic.im-cdn.it/plan/135549880/xxl.jpg"],
  "publishedDate": "2026-05-14T17:50:10+00:00",
  "updatedDate": "2026-10-03T17:08:09+00:00",
  "description": "TRE LOCALI IN BOCCONI/RIPAMONTI\n\nZona vicinanze Bocconi. ...",
  "headline": "SPLENDIDO TRE LOCALI SIGNORILE",
  "reference": "40607",
  "characteristics": {"Tipologia": "Appartamento | Intera proprietà | Classe immobile signorile", "Disponibilità": "libero", "Box, posti auto": "1 in box privato/box in garage", "...": "..."},
  "yearBuilt": 2012,
  "availability": "libero",
  "kitchen": "Cucina a vista",
  "energyClass": "A",
  "energyConsumption": "≥ 175 kWh/m² anno",
  "condominiumFees": 2,
  "costs": [{"label": "Spese condominio", "value": "€ 2/mese"}, {"label": "Spese riscaldamento", "value": "€ 1/anno"}],
  "hasVirtualTour": true,
  "virtualTourUrl": "https://tour360.getrix.it/vrtours/2539695/",
  "videoUrls": ["https://youtube.com/embed/_2YMUtkYG28"],
  "agentName": "GASTONE DI PAOLA",
  "agencyAddress": "Via RUCELLAI 37 /via VERRO 33 37, Milano",
  "units": [],
  "sourceUrl": "Milano",
  "scrapedAt": "2026-10-12T19:30:00+00:00",
  "detailError": null
}
```

Without details the same keys are present; the detail-only ones are `null` or empty.

### Monitor changes on a schedule

Turn on **Only new or changed items** to watch a search: each run remembers what earlier runs returned and outputs only listings that are new or whose asking price changed. Unchanged listings are dropped before any detail request, so they are not charged. **Max items** then sets how many listings are checked per run; use the **Newest** sort so new listings land in that window (20 listings = one request). Save the input as a task and add a Schedule in Apify (for example daily), and give each watch its own **Monitoring key** if several schedules use the same search. Every output item has `changeType` (`new` or `changed`), and changed items have `previousValues` with the old values, e.g. `{"price": 598000}`.

### How much does it cost to scrape Immobiliare.it?

Pay per event, plus the Apify platform usage of your run:

- **Listing** (`result`): **$0.0007** per listing saved ($0.70 per 1,000).
- **Listing details** (`detail`): **+$0.0007** per listing whose detail record was fetched (Include listing details, or listing URLs).

There is no start fee. Platform usage is small because the actor uses plain HTTP and the standard datacenter proxy: measured on Apify, 1,000 listings without details took 51 requests, 100 seconds and about **$0.006** of usage; with details about **$0.03 per 1,000** listings. So 1,000 listings cost about $0.71 in total, or about $1.43 with details.

### Can I scrape a whole town, a zone or an agency?

Yes. A town search covers every page (Milano has about 19,000 homes for sale; Roma about 25,700), 20 listings per request. Narrow a large town with a zone (`Navigli, Milano`, or the zone filter on the website, which adds `idMZona[]` to the URL) or with price, size and rooms filters. Zone searches return what Immobiliare.it shows for that zone, which can include a few listings just across the border labelled with the neighbouring zone. An agency page URL returns all of that agency's active listings. Provinces work as `provincia di Bergamo` or a `/vendita-case/bergamo-provincia/` URL. Region-wide searches are not supported: list the provinces you need.

### Does it work without login or a residential proxy?

Yes. The actor reads the same listing data the Immobiliare.it apps use, so no login, cookies or browser are needed, and the default Apify datacenter proxy works. If a run ever reports `blockedAnswers` in its SUMMARY, switch the proxy to Residential (Italy).

### Which filters from Immobiliare.it URLs are supported?

Town, province, zone (`idMZona[]`) and micro-zone (`idQuartiere[]`), sale or rent, property type from the URL path (case, appartamenti, ville, attici, uffici, negozi, box, terreni, capannoni, magazzini, palazzi, nuove costruzioni, camere), `prezzoMinimo`/`prezzoMassimo`, `superficieMinima`/`superficieMassima`, `localiMinimo`/`localiMassimo`, and the sort (`criterio` + `ordine`: prezzo, superficie, dataModifica, locali). Other website filters (bathrooms, floor, garden...) are ignored and listed in the run's SUMMARY under `ignoredUrlParams`; filter those fields in the dataset instead.

### Integrations and API

Use the run in Zapier, Make, n8n, Google Sheets, or call it from Python/Node with the Apify client. See the **API** tab for ready-made snippets.

### Is it legal to scrape Immobiliare.it?

The actor collects publicly listed property data (prices, sizes, locations, photos, agency business details) that anyone can see without logging in. It does not collect phone numbers, e-mail addresses, private sellers' names or private sellers' street addresses. You are responsible for how you use the data: check Immobiliare.it's terms of use, respect copyright on photos and descriptions, and follow the GDPR if you process data about people (for example agents' names).

# Actor input Schema

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

Immobiliare.it pages: a search with your filters (run it on immobiliare.it and copy the address bar, e.g. https://www.immobiliare.it/vendita-case/milano/?prezzoMassimo=400000), a listing (https://www.immobiliare.it/annunci/133212744/) or an agency page (https://www.immobiliare.it/agenzie-immobiliari/51157/...) for all its listings. Price, size, rooms, zone and sort filters in the URL are applied. Leave empty to use Locations.

## `locations` (type: `array`):

Alternative to Start URLs: Italian towns (comuni), provinces or zones, e.g. "Milano", "Roma", "provincia di Firenze", "Navigli, Milano". Searches with the type and filters below. Region-wide searches are not supported: list provinces instead.

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

Used with Locations.

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

Used with Locations. 'Homes' covers every residential type (apartments, villas, penthouses, houses...).

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

Used with Locations. Sale price, or monthly rent for rentals.

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

Used with Locations. Sale price, or monthly rent for rentals.

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

Used with Locations.

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

Used with Locations.

## `minRooms` (type: `integer`):

Used with Locations. Italian listings count rooms as 'locali' (bilocale = 2, trilocale = 3; kitchen and bathrooms not counted).

## `maxRooms` (type: `integer`):

Used with Locations.

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

Used with Locations. Immobiliare.it lists paid placements (Sky, Top, Premium...) first in every order. 'Newest' is the natural choice for monitoring.

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

Also fetch each listing's detail record: full description, headline, all characteristics, year built, availability, kitchen, heating and air conditioning, condominium fees and other costs, energy class, virtual tour and videos, agent name, agency URL and address, new-build units. One extra request and an extra 'detail' event per listing.

## `language` (type: `string`):

Italian returns descriptions and labels as published. English returns Immobiliare.it's own English labels and machine-translated descriptions, and /en/ listing URLs.

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

Stop after this many listings in total (all searches together). Keeps cost predictable. With 'Only new or changed items' on, this is the number of listings checked per run (e.g. the newest 20).

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

Stop each search after this many result pages (20 listings each). Every page of a search is reachable.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this search returned and output only new items or items whose price/status changed. Use with a Schedule; unchanged items are not charged.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input

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

Apify Proxy. The default datacenter proxy works: the actor reads Immobiliare.it's app API, not the protected website. Switch to Residential (Italy) only if runs report blocked answers.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.immobiliare.it/vendita-case/milano/"
    }
  ],
  "operation": "sale",
  "propertyType": "homes",
  "sort": "",
  "includeDetails": false,
  "language": "it",
  "maxItems": 100,
  "maxPagesPerSearch": 100,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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": [
        {
            "url": "https://www.immobiliare.it/vendita-case/milano/"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/immobiliare-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.immobiliare.it/vendita-case/milano/" }],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/immobiliare-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.immobiliare.it/vendita-case/milano/"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call tortuga/immobiliare-scraper --silent --output-dataset

```

## MCP server setup

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