# Property Listings API - Europe Prices, Comps, Market Data (`nabeelbaghoor/european-property-listings-api`) Actor

Search European property listings deduplicated across every portal advertising them, with sale and rent prices side by side, full price change history, days on market, comparables, valuation price bands and market time series.

- **URL**: https://apify.com/nabeelbaghoor/european-property-listings-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Real estate, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.00 / 1,000 property record returneds

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?

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

## Property Listings API - Europe Prices, Comps, Market Data

Search European residential property where the same home is one record rather than nine, with the sale and rent sides side by side, the full price change history, and the comparables and market analytics around it.

### What this actor does

- **One row per property, not per advert.** The provider deduplicates the same home across every portal and agency advertising it. Each row carries the number of separate adverts for that one property and the agencies and portals behind them, which is itself a signal: a home on one portal is a quiet listing and the same home on nine is not.
- **Sale and rent side by side.** A property can be advertised for both at once, so the sale price, sale status and sale days on market sit next to the rent price, rent status and rent days on market rather than being folded into one column.
- **Price change history.** Every recorded price change with the date, the old price and the new one, plus the total change and the size of the most recent reduction, which is how you find where negotiation room is.
- **Comparables and a valuation band.** Give an address, a point or a Spanish cadastral reference and get the comparable properties with a similarity score and an outlier flag, or a price band of three points: fast sell, fair market and out of market.
- **Market analysis and time series.** Average price and price per square metre for an area, the trailing 1, 3, 6, 9 and 12 month figures, and any of seven measures plotted over weeks, months, quarters or years.
- **Filter on 30+ real attributes.** Market status including sold and rented, property type, condition, price and price per square metre, bedrooms, bathrooms, rooms, total, living and plot area, construction year, days on market, gross yield, parking, 13 characteristics, floor position, view, facing direction, energy rating, and the private seller, bank owned, auction and exclusive flags.
- **Run daily deltas.** Filter by updated since or first seen since, so a scheduled run returns only what moved rather than the whole set.
- **Pay per result.** You are charged for rows that carry a real record. Targets the provider cannot price are free.

### Bring your own credentials

This provider authenticates with an **account email and password** rather than an API key, and exchanges them for an access token. Paste yours into the API account email and API password fields, or set them as the `DATA_API_USERNAME` and `DATA_API_PASSWORD` secrets. If you keep one combined secret across actors, `DATA_API_KEY` set to `email:password` also works.

Both halves are stored as secrets. The actor signs in once per run rather than once per request, and refreshes the token silently if a long run outlasts it.

### Start here

Property search, market analysis and time series are addressed by the provider's numeric location IDs, and location search is how you get them. Run it first with a place name, read the `locationId` column, then feed those into the mode you want. Reference mode returns the provider's own property type and characteristic vocabularies, which is where the exact spellings those filters expect come from.

### Example output

```json
{
  "recordType": "property",
  "requested": "location IDs 12345",
  "found": true,
  "propertyId": "8412907",
  "type": "apartment",
  "typeGroup": "apartment",
  "condition": "very-good",
  "address": "Rua Garrett 42",
  "zipCode": "1200-204",
  "locationName": "Chiado, Lisboa",
  "latitude": 38.71085,
  "longitude": -9.14094,
  "totalArea": 118,
  "livingArea": 104,
  "bedrooms": 2,
  "bathrooms": 2,
  "rooms": 4,
  "constructionYear": 1946,
  "energyRating": "C",
  "saleStatus": "active",
  "salePrice": 795000,
  "salePricePerSqm": 6737,
  "saleCurrency": "EUR",
  "rentStatus": "none",
  "grossYield": 4.1,
  "saleDaysOnMarket": 87,
  "saleHistoryHasGap": false,
  "totalSalePriceChange": -40000,
  "lastSalePriceReduction": -25000,
  "listingCount": 6,
  "agencies": "Century 21 Chiado, Remax Prime, Porta da Frente",
  "characteristics": "elevator, terrace, storage"
}
```

### FAQ

#### Why is one property sometimes advertised by six agencies?

Because in these markets a seller often instructs several agencies at once, and each one advertises on several portals. The provider resolves all of those adverts back to the single home they describe, so you get one row with an `listingCount` of six and the agency names beside it, rather than six rows that look like six properties. Deduplicating listing data yourself is the hardest part of this problem and the main reason to use an aggregator for it.

#### What is the difference between the three valuation prices?

They are three points on one curve. The fast sell price is roughly what the property would go for in a hurry, the fair market price is what the provider thinks it is worth, and the out of market price is the level at which it stops attracting buyers. The spread between them is the useful part: a narrow spread means a liquid, well evidenced market and a wide one means the opposite.

#### Can I get sold prices rather than asking prices?

Yes. Set the market status filter to sold or rented. Those are properties the provider watched leave the market, with the `soldAt` or `rentedAt` date and the last price they were advertised at. That is not the same thing as a registry transfer price, so treat it as the final asking price rather than the achieved price.

#### What does the history gap flag mean?

The provider records whether a property's listing history has a period it was not tracking. When `saleHistoryHasGap` is true, the days on market figure is a floor rather than a fact: the property was on the market for at least that long and possibly longer. A 40 day count with a gap and a real 40 day count are different claims, so the flag travels next to the number rather than being dropped.

#### How do I find stale listings with negotiation room?

Set a high minimum days on market and sort by last update ascending. Then look at `lastSalePriceReduction` and `totalSalePriceChange`: a property that has been listed for 200 days and already come down twice is a different conversation from one that has been listed for 200 days at an unchanged price.

#### How do I track a neighbourhood over time?

Use time series mode with the location IDs, pick a measure and an interval. Average price per square metre by quarter is the standard read; new listings and sold or rented per month tell you about supply and absorption rather than price; price increases and price reductions per month are the fastest indicator that a market has turned.

#### Which countries does this cover?

Coverage is strongest across Southern and Western Europe, and the location search suggests within Spain and Portugal by country code. Which countries your own account can see depends on what you licensed, and a country outside your plan is refused rather than returning nothing.

#### Why do I need location IDs instead of just typing a city?

Because a place name is ambiguous and an ID is not. Location search returns the ID, the administrative level and the breadcrumb trail for every match, so you can tell a municipality from the district of the same name before you spend a search on it. A latitude, longitude and radius works instead when you want a circle rather than an administrative boundary.

#### How deep can a search page?

The provider caps paging at an offset of 50000 rows and 100 rows per request. The actor pages automatically, stops at your row cap, and warns you if it reaches the provider's ceiling rather than silently returning a partial set. To reach records past it, narrow the filters rather than raising the row cap.

#### What does it cost?

Pricing is pay per result: 0.012 USD per property, 0.02 USD per valuation price band, 0.01 USD per market analysis or series point, and 0.005 USD per location or reference entry, with Apify platform usage included. Rows the provider found nothing for are not charged. You also need your own account with the data provider, which bills on its own plans.

#### Does the actor fail if my credentials are wrong?

No. Missing credentials, a rejected email or password, an endpoint or country outside your plan, or an empty input all end the run cleanly with a message saying what to change. Only a real outage or a bug fails the run.

### Keyword map

European property listings API, property data API Europe, Spain property API, Portugal property data, Lisbon property prices, Madrid property data, real estate listings aggregator, deduplicated property listings, property price history API, days on market Europe, property comparables API, real estate valuation API, price per square metre, bank owned property, auction property data, gross yield property, market time series real estate, property market analytics

# Actor input Schema

## `mode` (type: `string`):

Location search finds the provider's location IDs, which the search and analytics modes are addressed by, so run it first. Property search pages the deduplicated property set. Comparables finds similar properties around a target and valuation turns them into a price band. Market analysis reads the aggregate picture for an area and time series plots one measure over time.

## `locationNames` (type: `array`):

Location search mode. One partial place name per line, for example Lisbon or Marbella. Each one returns matching locations with the numeric location IDs the other modes need.

## `countryCodes` (type: `array`):

Location search mode. Narrows the suggestions to these countries.

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

Location search mode. The language location names are returned in.

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

One numeric location ID per line, as returned by location search. Used by property search, market analysis and time series. In reference mode the first one narrows the sources list.

## `propertyIds` (type: `array`):

Property by ID mode. One numeric property ID per line, as returned in the propertyId column of a search.

## `addresses` (type: `array`):

Comparables and valuation modes. One address per line to centre the comparables search on. Each address is run separately.

## `cadastralReference` (type: `string`):

Comparables and valuation modes, Spain only. An alternative to an address or coordinates, and the most precise way to name a Spanish property.

## `latitude` (type: `string`):

An alternative to location IDs or an address. Give a longitude and a radius with it.

## `longitude` (type: `string`):

An alternative to location IDs or an address. Give a latitude and a radius with it.

## `radiusMeters` (type: `integer`):

How far from the point to search, in metres. Location IDs take precedence over a radius, because the provider accepts only one boundary shape per request.

## `searchOperations` (type: `array`):

Property search mode. Which side of the market to return. Sale and rent are what is available now; sold and rented are completed, which is how to study achieved prices; hold is reserved or under offer.

## `propertyTypes` (type: `array`):

Keeps only these types, in the provider's own spelling, for example apartment, house, villa, townhouse, penthouse, studio, duplex, country\_house, plot, office or retail. Reference mode returns the full list.

## `conditions` (type: `array`):

Keeps only properties in these conditions.

## `condition` (type: `string`):

Comparables and valuation modes. Describes the property being priced, rather than filtering a search.

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

In the local currency of the listing.

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

In the local currency of the listing.

## `pricePerSqmFrom` (type: `integer`):

Often a better comparison across a city than the asking price itself.

## `pricePerSqmTo` (type: `integer`):

Often a better comparison across a city than the asking price itself.

## `bedroomsFrom` (type: `integer`):

Property search, market analysis and time series modes.

## `bedroomsTo` (type: `integer`):

Property search, market analysis and time series modes.

## `bedrooms` (type: `integer`):

Comparables and valuation modes. Describes the property being priced.

## `bathroomsFrom` (type: `integer`):

Property search, market analysis and time series modes.

## `bathroomsTo` (type: `integer`):

Property search, market analysis and time series modes.

## `bathrooms` (type: `integer`):

Comparables and valuation modes. Describes the property being priced.

## `roomsFrom` (type: `integer`):

Total rooms rather than bedrooms, which is how several of these markets quote size.

## `roomsTo` (type: `integer`):

Total rooms rather than bedrooms, which is how several of these markets quote size.

## `rooms` (type: `integer`):

Comparables and valuation modes. Describes the property being priced.

## `totalAreaFrom` (type: `integer`):

In square metres, including terraces and outbuildings.

## `totalAreaTo` (type: `integer`):

In square metres, including terraces and outbuildings.

## `totalArea` (type: `integer`):

Comparables and valuation modes. In square metres. One of the strongest inputs to a price band.

## `livingAreaFrom` (type: `integer`):

In square metres, interior only.

## `livingAreaTo` (type: `integer`):

In square metres, interior only.

## `plotAreaFrom` (type: `integer`):

In square metres. Applies to houses and land.

## `plotAreaTo` (type: `integer`):

In square metres. Applies to houses and land.

## `plotArea` (type: `integer`):

Comparables and valuation modes. In square metres.

## `constructionYearFrom` (type: `integer`):

A four digit year.

## `constructionYearTo` (type: `integer`):

A four digit year.

## `constructionYear` (type: `integer`):

Comparables and valuation modes. Describes the property being priced.

## `daysOnMarketFrom` (type: `integer`):

A high floor finds stale stock, which is where negotiation room usually is.

## `daysOnMarketTo` (type: `integer`):

A low ceiling finds fresh listings.

## `grossYieldFrom` (type: `integer`):

Property search mode. The provider's own gross yield estimate, as a percentage.

## `grossYieldTo` (type: `integer`):

Property search mode. The provider's own gross yield estimate, as a percentage.

## `parkingsFrom` (type: `integer`):

Property search mode.

## `parkingsTo` (type: `integer`):

Property search mode.

## `mustHave` (type: `array`):

Keeps only properties with all of these.

## `exclude` (type: `array`):

Keeps only properties without any of these. Anything named on both lists is treated as required, since that is the deliberate half of the contradiction.

## `floors` (type: `array`):

Keeps only properties on these floors.

## `views` (type: `array`):

Keeps only properties with these views.

## `directions` (type: `array`):

Keeps only properties facing these ways, which in southern Europe is a real price factor.

## `orientation` (type: `string`):

Whether the property faces outward from the building or into a courtyard.

## `energyRatings` (type: `array`):

Keeps only properties with these energy certificate ratings.

## `updatedFrom` (type: `string`):

Property search mode. Keeps only properties whose record changed on or after this date, as YYYY-MM-DD. This is how to run a daily delta rather than the whole set.

## `createdFrom` (type: `string`):

Property search mode. Keeps only properties the provider first indexed on or after this date, as YYYY-MM-DD, which is how to find genuinely new stock.

## `privateOnly` (type: `boolean`):

Leave this alone to ignore it. Ticked keeps only properties advertised by private owners rather than agencies.

## `auctionOnly` (type: `boolean`):

Leave this alone to ignore it.

## `bankOnly` (type: `boolean`):

Leave this alone to ignore it. Bank owned stock is repossessed property being sold by the lender.

## `exclusiveOnly` (type: `boolean`):

Leave this alone to ignore it. An exclusive property is advertised by one agency rather than several.

## `withAgencies` (type: `array`):

Property search mode. One agency name per line. Reference mode returns the agency list.

## `withoutAgencies` (type: `array`):

Property search mode. One agency name per line, which is how to exclude your own stock from a competitor scan.

## `comparablesCount` (type: `integer`):

Comparables and valuation modes. How many similar properties to base the answer on. More comparables is a steadier estimate over a looser match.

## `comparablesOperation` (type: `string`):

Comparables and valuation modes. Which side of the market to draw the comparables from.

## `soldOrRentedAfter` (type: `string`):

Comparables and valuation modes. As YYYY-MM-DD. A tighter window tracks a moving market more closely but finds fewer comparables.

## `excludeOutliers` (type: `boolean`):

Comparables, valuation and time series modes. Drops the properties the provider considers unrepresentative, which steadies an estimate in a thin market.

## `businessType` (type: `string`):

Market analysis and time series modes. Which side of the market to analyse.

## `dataPoint` (type: `string`):

Time series mode. Which measure to plot over time.

## `dateInterval` (type: `string`):

Time series mode. How the series is bucketed. A quarter smooths a thin market better than a week does.

## `seriesFrom` (type: `string`):

Time series mode. As YYYY-MM-DD.

## `seriesTo` (type: `string`):

Time series mode. As YYYY-MM-DD.

## `referenceLists` (type: `array`):

Reference mode. The provider's own vocabularies, which is where the exact spellings for the property type and characteristic filters come from.

## `agencyName` (type: `string`):

Reference mode, agencies list only. A partial agency name to search for.

## `orderBy` (type: `string`):

Property search mode.

## `orderDirection` (type: `string`):

Property search mode.

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

The most rows this run will save and charge for. This is the spending control on both sides.

## `pageSize` (type: `integer`):

How many rows to ask for at a time, up to the provider's ceiling of 100. The provider also caps paging at an offset of 50000, so a larger result set than that needs tighter filters.

## `requestsPerMinute` (type: `integer`):

How fast to call the provider. Lower this if your plan is metered tightly.

## `baseUrl` (type: `string`):

Only set this if the provider has given you a different host. Leave empty to use the documented one.

## `apiUsername` (type: `string`):

Your own account email for the property listings provider. This actor is bring your own credentials: it never uses anyone else's, and both halves are stored as secrets.

## `apiPassword` (type: `string`):

Your own account password for the property listings provider. Exchanged once per run for an access token rather than sent with every request.

## Actor input object example

```json
{
  "mode": "locationSearch",
  "language": "en",
  "radiusMeters": 2000,
  "comparablesCount": 10,
  "comparablesOperation": "sale",
  "excludeOutliers": false,
  "businessType": "sale",
  "dataPoint": "avg_price",
  "dateInterval": "month",
  "orderBy": "last_update",
  "orderDirection": "desc",
  "maxResults": 1000,
  "pageSize": 100,
  "requestsPerMinute": 120
}
```

# Actor output Schema

## `records` (type: `string`):

One row per record, alongside the location, target or area that produced it.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/european-property-listings-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/european-property-listings-api").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 '{}' |
apify call nabeelbaghoor/european-property-listings-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/european-property-listings-api"
        }
    }
}
```

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/LtHvumMMcwZhhUzBu/builds/lo0rXAp2WwrnHtLjB/openapi.json
