Idealista Scraper - Spain, Italy and Portugal Property Listings avatar

Idealista Scraper - Spain, Italy and Portugal Property Listings

Pricing

from $0.50 / 1,000 listings

Go to Apify Store
Idealista Scraper - Spain, Italy and Portugal Property Listings

Idealista Scraper - Spain, Italy and Portugal Property Listings

Collect Idealista houses and flats for sale or rent in Spain, Italy and Portugal: price, price drops, size, rooms, floor, agency and listing URL.

Pricing

from $0.50 / 1,000 listings

Rating

0.0

(0)

Developer

Amadeusz

Amadeusz

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

18 hours ago

Last modified

Categories

Share

Idealista Scraper

Collect houses and flats for sale or rent from Idealista in Spain (idealista.com), Italy (idealista.it) and Portugal (idealista.pt). Type a province or town name, add the filters of the Idealista search form, or paste a search URL copied from Idealista. Every listing is checked against your filters, de-duplicated and returned as clean JSON, ready for CSV, Excel, the API or AI agents.

Run Idealista Scraper · Input schema · Output schema

What you get

  • Three markets: Spain, Italy and Portugal, houses and flats for sale or for rent.
  • Place names instead of codes: location takes a province, municipality, island, coast or region name (Madrid, Valencia, Milano, Lisbon, Mallorca, Costa del Sol, Toscana, Algarve, accents optional) or the path from the Idealista address bar (barcelona/eixample). A misspelled name ends the run with the closest names. A listLocations run (start fee only) lists every name and path.
  • Every option of the search form: property type, bedrooms (rooms in Italy), bathrooms, condition, lift, terrace, parking, pool, floor, energy rating, floor plan, virtual tour and more. A listFilters run (start fee only) lists every key per country and operation; an unknown key gets the closest keys in the error message.
  • Price and size ranges, publication date (publishedWithin), 14 sort orders, search URLs copied from Idealista (startUrls), several searches in one run with shared de-duplication.
  • Verified results: the filters and the sort order are checked against the search Idealista actually applied before anything is charged. A filter Idealista would silently ignore stops the run with an explanation instead of returning wrong data, and a card that contradicts your price, size, bedroom or lift filter is dropped without a charge.
  • No duplicates: the same listing is returned once per run, and you are charged once.
  • Price drops from the card: previousPrice and priceDropPercent, plus price per m², floor, lift, parking and the agency.
  • Optional details (enrichDetails): full description, all features, bathrooms, condition, year built, energy certificate, community costs, address, coordinates, all photos, floor plans, update date and advertiser.
  • Monitoring (mode: monitor): each run returns only listings not reported by earlier runs, plus a change record when the price of a reported listing changed.
  • Estimate (countOnly): how many listings match your search, without listing charges.
  • More than 1,800 results per search: one Idealista search shows at most 1,800 listings, so the Actor splits larger searches by price automatically.
  • One date format (UTC, YYYY-MM-DDTHH:mm:ssZ) in every field.

Quick start

  1. Choose Sale or rent and type a Location, for example Madrid, Valencia, Milano or Lisbon.
  2. Add price, size or filters if you need them.
  3. Set Max items (the limit of listings returned and charged).
  4. Click Start. The default input (location: Madrid, operation: sale, maxItems: 100) is a working example.
  5. Open the Overview table, export JSON, CSV or Excel, or read the data through the API.

Not sure how many listings match? Start with Count only.

Pricing

Pay per event:

EventPriceWhen
listing$0.50 / 1000One per listing returned
listing-details$1.00 / 1000One per listing when enrichDetails is on and the listing page was read
listing-change$0.50 / 1000One per change record in monitor mode
Actor start$0.005 per runOnce per run with the default 1 GB of memory (once more per extra GB)

Duplicates, rejected cards and listings removed before their page was read are not charged. countOnly, listLocations and listFilters runs, empty runs and runs that end with an input or filter error return no listings and pay only the start. maxItems and your maximum cost per run are hard limits: the run stops when either is reached.

Cost of a run = $0.005 + listings × $0.0005 (plus $0.001 per enriched listing and $0.0005 per change record). For example, 1,000 listings with details cost $1.505.

Ready-to-use recipes

1. Flats for sale in a city

{
"country": "es",
"operation": "sale",
"location": "Valencia",
"filters": ["flats", "lift", "bedrooms-2", "bedrooms-3"],
"priceTo": 300000,
"sizeFrom": 70,
"maxItems": 200
}

Keys of one group (bedrooms-2, bedrooms-3) mean either; keys of different groups must all apply.

2. Rentals in Milan published this week

{
"country": "it",
"operation": "rent",
"location": "Milano",
"filters": ["rooms-2", "furnished"],
"publishedWithin": "week",
"priceTo": 1800,
"maxItems": 100
}

Italy counts rooms (rooms-2 is a "bilocale"), Spain and Portugal count bedrooms (bedrooms-2).

3. Search URL copied from Idealista

{
"startUrls": ["https://www.idealista.com/alquiler-viviendas/madrid-madrid/con-precio-hasta_1500,ascensor/"],
"maxItems": 200
}

The country, operation, location, filters and sort come from the URL. A URL part the Actor does not know is rejected instead of ignored.

4. Monitor new listings and price changes

{
"country": "pt",
"operation": "sale",
"location": "Lisbon",
"filters": ["bedrooms-2"],
"priceTo": 500000,
"mode": "monitor",
"maxItems": 500
}

Schedule it in Apify. The first run returns what is there; later runs return only new listings and change records.

Use it from the API

curl -X POST "https://api.apify.com/v2/acts/ziomixshot~idealista-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"location":"Madrid","operation":"rent","priceTo":1500,"maxItems":50}'

The same Actor is available to AI agents through the Apify MCP server: add ziomixshot/idealista-scraper to the tools of the server.

Input

The full description of every field is in the input form. A field the Actor does not know (for example a typo such as locaton) is named in a warning in the run log and ignored.

FieldDescription
countryes, it or pt. Empty: taken from the location name; a location path without a country searches Spain.
operationsale (default) or rent.
locationProvince, municipality, zone (island, coast, comarca: Mallorca, Tenerife) or region name (Costa del Sol, Toscana, Algarve) (a municipality wins over a province, a zone or a region with the same name; of several municipalities the provincial capital wins and the log names the others; a country or province after a comma picks one: Porto, Spain, Arroyomolinos, Madrid), or the path from the Idealista address bar (madrid-madrid, barcelona/eixample, geo/costa-del-sol). A name the catalog does not have ends the run with the closest names, and so does a path Idealista does not know.
filtersFilter keys of the search form in any letter case, for example lift, terrace, flats, bedrooms-2 (Spain, Portugal) or rooms-2 (Italy). listFilters shows the keys of each country and operation.
publishedWithin24h, 48h, week or month.
priceFrom, priceToPrice in EUR, inclusive: sale price, or monthly rent for rent.
sizeFrom, sizeToBuilt area in m², inclusive.
sortByrelevance, priceAsc, priceDesc, newest, oldest, biggestPriceDrop, pricePerM2Asc, pricePerM2Desc, sizeDesc, sizeAsc, floorDesc, floorAsc, privateAdvertisersFirst, professionalsFirst. An order a country or operation does not offer is refused.
startUrlsSearch URLs copied from Idealista. They replace all search, filter and sort fields above.
maxItemsHard limit of returned listings and charges, shared by all searches of the run (default 100, up to 100,000).
enrichDetailsRead the page of each listing; each one also triggers the listing-details event.
mode, seenStoreName, seenIdsKeyMonitoring, see below.
countOnlyReturn only the number of matching listings.
listLocationsReturn the provinces, municipalities, zones and regions (name, path, search URL) instead of listings; country and location narrow the list. When both listLocations and listFilters are set, only locations are listed.
listFiltersReturn the filter keys and sort orders instead of listings; country and operation narrow the list.

Output

Every listing is one dataset item. The dataset has three views: Overview, Details and Price changes.

Fields of every listing item: recordType, id, url, title, country, operation, price, currency (EUR), previousPrice, priceDropPercent, pricePerM2, bedrooms (Spain, Portugal), rooms (Italy), sizeM2, floor, hasLift, exterior, parking, parkingPrice, publishedLabel, otherDetails, descriptionSnippet, tags, ribbon (for example "Finished new development"), photoCount, thumbnail, isProfessional, highlight (top, featured or null), agency (name, profileUrl, logoUrl), publishedAt, scrapedAt. A value Idealista does not show is null.

Added by enrichDetails: detailsFetched, description, descriptionTranslated, locationName, address (from street to province), latitude, longitude, locationId, builtAreaM2, usableAreaM2, bathrooms, condition, yearBuilt, orientation, heating, energy (rating A to G, plus A+, A1 to A4 and B- where the country uses them; consumptionKwhM2Year, emissionsKgCo2M2Year), communityCosts, priceNotes, features (every section of the listing page), images (url, tag), floorPlans, updatedAt, reference, advertiser (type, name, agencyName, agencyProfileUrl, agencyLogoUrl). A listing removed before its page was read is skipped and not charged (detailsUnavailable in the run status); a page that could not be read after all retries leaves the card record, charged only as listing (detailsFailed, run status PARTIAL).

Other record types, always free except change:

  • status: the run ended because of your input and returned no listings. Fields: status, message (what is wrong and how to fix it), details, scrapedAt.
  • estimate: result of countOnly: estimatedTotal, sources (one count per search), note.
  • location: result of listLocations: country, kind (province, municipality, zone or area for a region), name, path, province (of a municipality or zone), url. Put name or path into location.
  • filter and sort: result of listFilters: country, operation, key, inputField (filters or sortBy), group, label, slug.
  • change: see monitoring.

Run status

The OUTPUT record of the key-value store describes the run: status (OK, PARTIAL, INVALID_INPUT, URL_NOT_FULLY_RESOLVED, FILTER_VERIFICATION_FAILED, SEEN_STATE_INVALID, UPSTREAM_ERROR), counters (duplicates, detailsUnavailable, detailsFailed, predicateViolations) and jobs[] per search (url, expectedTotal, pagesRead, failedPages, coverage, partitions, truncatedPartitions). Input and filter errors end the run without listings. A valid search Idealista has no listings for ends with OK and 0 listings. UPSTREAM_ERROR with a first page blocked or failed after all retries charges only the start: run it again later. PARTIAL means some pages or details failed after all retries; the returned records are still valid.

Monitoring new listings and price changes

mode: monitor returns only listings whose ID was not reported by earlier runs with the same seenStoreName and seenIdsKey, and a change record for every reported listing whose price changed since: recordType: "change", metric: "price", id, url, title, country, operation, currency, previous, current, delta and changedAt.

  • Every run reads the whole search, because Idealista in Spain blocks the newest-first order. sortBy does not change what a monitor returns.
  • The first run returns every listing as new and stores its price. Later runs compare the price of each known card with the stored one, with no extra requests. A price that appears or disappears is stored without a change record.
  • maxItems limits new listings and change records together. New listings or changes beyond the limit are kept for the next run.
  • Without seenStoreName the Actor uses a store named after the search (idealista-monitor-..., shown in the run log), so repeating the same search keeps its history; the sort order and the order of filters do not change the name. Set a new name (letters, digits and hyphens) to share one history between different runs: the Actor creates that store and keeps access to it. A store created outside this Actor cannot be used, because the Actor runs with limited permissions; such a run stops before any listing is charged.
  • The state keeps the 500,000 newest listings with their last price. A corrupted state record ends the run with SEEN_STATE_INVALID instead of starting over.
  • The state is saved when a run ends. Runs on the same store must not overlap (for example a schedule shorter than one run): overlapping runs report the same listings, and an aborted or timed-out run may not save what it reported.

Estimate (countOnly)

Returns one item with recordType: "estimate" and estimatedTotal, the number Idealista reports for each search. The first page of each search is read, so an unknown location or a filter Idealista ignores still stops the run. With several startUrls the result is the sum of the searches (overlapping searches are counted once per search). The monitor state is not touched. No listing is charged.

Limits and notes

  • One search exposes at most 1,800 listings (60 pages of 30). For maxItems above that, larger searches are read in price ranges. Sorting then applies only inside each range. truncatedPartitions above 0 means more than 1,800 listings with the same price that cannot be separated.
  • In Spain Idealista blocks the newest sort order; use publishedWithin to get recent listings.
  • The Actor reads the English version of each site. descriptionTranslated: true means Idealista translated the description automatically.
  • Phone numbers are not collected: Idealista reveals them only after a click on the listing page.
  • Idealista protects its sites with anti-bot systems. Runs use Apify residential proxies in the country of the site (included in the price). A page that cannot be read after all retries is counted in failedPages and the run ends PARTIAL.
  • The Actor uses the public web pages of Idealista, which are unofficial as an interface and can change without notice.
  • The Actor reads public pages anonymously. It never logs in, never contacts advertisers, never saves searches and never changes anything on an account.

This Actor collects data that Idealista shows publicly. Idealista's terms of use restrict automated access to the site and the use of its content; you are responsible for using the data lawfully and in line with those terms. Listings contain personal data of advertisers (names of private advertisers and agents), so GDPR rules apply to how you store and use them. The Actor is not affiliated with Idealista.

Development

Stack: Node 20+, TypeScript strict (ESM), Apify SDK 3, Impit 0.7.5 (Chrome profile), cheerio 1.0.0-rc.12, Vitest, ESLint, Prettier. Apify RESIDENTIAL proxy of the country of the site. No database and no migrations.

npm ci
npm run typecheck && npm run lint && npm run format:check && npm test

Local run (needs APIFY_TOKEN in the environment and RESIDENTIAL proxy on the account):

mkdir -p storage/key_value_stores/default
echo '{"country":"es","location":"madrid-madrid","maxItems":60}' > storage/key_value_stores/default/INPUT.json
npm run start:dev

Results: storage/datasets/default/*.json and storage/key_value_stores/default/OUTPUT.json.

VariableRole
APIFY_TOKENApify token: locally it fetches the proxy password; in CI a GitHub secret for the deployment

The location catalog (src/idealista/locations/*.json: provinces and municipalities with English names, zones linked from province pages and /geo/ regions) is built offline by a read-only script, about 190 requests through the RESIDENTIAL proxy:

npx tsx --env-file=<file with APIFY_TOKEN> scripts/build-locations.ts es it pt
npx prettier --write src/idealista/locations

A push to main runs .github/workflows/deploy-apify.yml: typecheck, lint, format, tests, schema validation and an Actor build tagged beta.

Backlog and decisions: docs/backlog.md, architecture: docs/diagram.md, routes and robots.txt: docs/ENDPOINTS.md, competitors and pricing: docs/pricing.md.