# France Property Sales | Official DVF Prices & EUR per m2 (`alinz/france-property-prices`) Actor

Real French property sale prices from the official DVF open dataset. Correctly deduplicated to one row per transaction, with price per square metre, surface, rooms and coordinates. Search any department or commune, or get median EUR/m2 by commune and quarter.

- **URL**: https://apify.com/alinz/france-property-prices.md
- **Developed by:** [אלון זריהן](https://apify.com/alinz) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 results

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

## France Property Sales — Official DVF Prices and €/m²

Real sale prices for French property, from **DVF** (*Demandes de Valeurs Foncières*) — the open dataset the French tax administration publishes for every registered property transfer.

Not asking prices. What buyers actually paid, as recorded by the land registry, with **price per square metre** computed correctly.

### Coverage

| | |
|---|---|
| Geography | All 97 departments — mainland, Corsica (2A/2B), overseas (971-974) |
| Years | 2021 to 2025 |
| Granularity | Department, commune (INSEE code), or postcode prefix |
| Per sale | Price, €/m², surface, rooms, address, postcode, coordinates |
| Statistics | Median and quartile €/m² by commune, quarter and property type |

### The correctness problem this Actor solves

**DVF publishes one row per lot, not per sale.** A single transaction repeats its full price on every row it touches. In Paris for 2024, **55% of transactions span more than one row**, and one building sale spanned **769 rows** all carrying the same €21,606,678.

Add the raw rows up and you get **€279.6 billion** where the real total is **€25.5 billion** — a **996% overstatement**. On top of that, 10.6% of rows are exact duplicates of another row in the same transaction, so naive surface totals are inflated too.

This Actor never emits raw rows. It groups by transaction, drops duplicate lots, sums only genuine living space, and returns **one row per actual sale**. Everything downstream — every €/m², every median — is computed on that corrected basis.

A sanity check you can verify yourself: run statistics mode on Paris and the most expensive arrondissements come out **6e, 1er, 7e, 8e, 4e**, at roughly €12,500-13,850/m². That is the real ranking of the Paris market.

### Output

**`transactions`** — one row per sale.

```json
{
  "mutationId": "2024-1193219",
  "date": "2024-01-04",
  "quarter": "2024-Q1",
  "price": 299120,
  "pricePerSqm": 8309,
  "propertyType": "Appartement",
  "habitableSurface": 36,
  "rooms": 3,
  "lotCount": 1,
  "dependencyCount": 0,
  "isMultiLot": false,
  "saleType": "Vente",
  "address": "17 B RUE DU ROI D ALGER",
  "postcode": "75018",
  "communeCode": "75118",
  "commune": "Paris 18e Arrondissement",
  "departmentCode": "75",
  "parcelId": "75118000BF0053",
  "longitude": 2.347665,
  "latitude": 48.895338
}
```

**`stats`** — median €/m² per commune, quarter and type.

```json
{
  "commune": "Paris 6e Arrondissement",
  "communeCode": "75106",
  "quarter": "2024-Q3",
  "propertyType": "Appartement",
  "salesCount": 68,
  "medianPricePerSqm": 13846,
  "p25PricePerSqm": 11846,
  "p75PricePerSqm": 16735,
  "medianPrice": 745000,
  "medianSurface": 54
}
```

### Examples

**Median €/m² across Paris, by arrondissement and quarter:**

```json
{
  "departments": ["75"],
  "years": ["2024"],
  "propertyTypes": ["Appartement"],
  "outputFormat": "stats",
  "minSalesPerGroup": 20
}
```

**Comparable sales in a price band on the Côte d'Azur:**

```json
{
  "departments": ["06"],
  "years": ["2024"],
  "propertyTypes": ["Appartement", "Maison"],
  "minPrice": 300000,
  "maxPrice": 800000,
  "minSurface": 50
}
```

**One arrondissement, four years of history:**

```json
{
  "departments": ["75"],
  "years": ["2021", "2022", "2023", "2024"],
  "communeCodes": ["75118"],
  "outputFormat": "stats"
}
```

**Greater Paris apartment market:**

```json
{
  "departments": ["75", "92", "93", "94"],
  "years": ["2024"],
  "propertyTypes": ["Appartement"],
  "outputFormat": "stats",
  "minSalesPerGroup": 30
}
```

### Field reference

| Field | Meaning |
|---|---|
| `mutationId` | DVF's transaction identifier — one per sale, after deduplication |
| `price` | Total transaction value in EUR (`valeur_fonciere`), counted once |
| `pricePerSqm` | Price ÷ habitable surface. Null when no living space is recorded |
| `habitableSurface` | Summed living area of apartments and houses only |
| `landSurface` | Summed land area where present |
| `rooms` | Summed principal rooms across habitable lots |
| `lotCount` / `dependencyCount` | Distinct lots in the sale, and how many are outbuildings |
| `isMultiLot` | True when the sale covers more than one lot — usually a building or portfolio |
| `propertyType` | Apartment or House when present; otherwise the dominant lot type |
| `saleType` | Ordinary sale, off-plan (VEFA), auction, exchange, or building land |
| `address` / `postcode` / `commune` / `communeCode` | Location as recorded |
| `parcelId` | Cadastral parcel reference |
| `longitude` / `latitude` | WGS84 coordinates, ready to map |

### Notes on the data

- **Outbuildings are excluded from €/m².** A `Dépendance` — garage, cellar, parking space — has no living area, so counting it would deflate the rate. It is reported separately as `dependencyCount`.
- **Multi-lot sales are excluded by default** and never enter the statistics. A whole-building sale is not comparable to a flat, and including it distorts a median badly.
- **Alsace-Moselle and Mayotte are absent.** Departments 57, 67 and 68 keep a separate *livre foncier* and are not covered by DVF. This is a property of the source, not of this Actor.
- **The most recent months are incomplete.** DVF is published in batches with a lag of several months.
- **Prices are the total transfer value**, which can cover several parcels at once — which is exactly why the deduplication above matters.

### Source and licence

Data from [files.data.gouv.fr/geo-dvf](https://files.data.gouv.fr/geo-dvf/), the geocoded DVF distribution published by Etalab. Open, unauthenticated, no access controls circumvented.

Licensed under the [Licence Ouverte / Open Licence 2.0](https://www.etalab.gouv.fr/licence-ouverte-open-licence). Attribution to the source is required when redistributing.

### Integrations

Output lands in a standard Apify dataset: export as **JSON, CSV, Excel or XML**, or connect it to **Make, Zapier, n8n, Google Sheets** or your own code via the Apify API. Coordinates are included, so results map directly.

# Actor input Schema

## `departments` (type: `array`):

French department codes: 01-95, plus 2A / 2B for Corsica and 971-974 overseas. 75 = Paris, 06 = Alpes-Maritimes (Nice, Cannes), 13 = Bouches-du-Rhône (Marseille), 69 = Rhône (Lyon), 33 = Gironde (Bordeaux), 92 = Hauts-de-Seine.

## `years` (type: `array`):

Published years: 2021 to 2025. Each department-year is one file, so more years means proportionally more work.

## `communeCodes` (type: `array`):

Optional. Narrow to specific communes by INSEE code, e.g. 75118 is the 18th arrondissement of Paris. Leave empty for the whole department.

## `postcodePrefix` (type: `string`):

Optional. Matches the start of the postcode, so '750' covers Paris and '75018' one arrondissement.

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

Leave empty for all. 'Dépendance' means a garage, cellar or parking space sold on its own.

## `saleTypes` (type: `array`):

'Vente' is an ordinary sale and is the sensible default. The others are off-plan sales, auctions, exchanges and building-land sales, which price differently.

## `excludeMultiLot` (type: `boolean`):

A transaction covering many lots is usually a whole building or a portfolio, not a comparable home. Keep this on for market analysis; turn it off to see every transfer.

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

Optional. Filters on the total transaction value, not the price per square metre.

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

Optional. Filters on the total transaction value, not the price per square metre.

## `minSurface` (type: `integer`):

Optional. Excludes outbuildings and parking spaces from the count, since only living space is measured.

## `outputFormat` (type: `string`):

'Transactions' returns one row per sale. 'Market statistics' returns median and quartile €/m² per commune, quarter and property type — the figures the French market actually quotes.

## `minSalesPerGroup` (type: `integer`):

Statistics mode only. Groups with fewer sales than this are dropped, because a median over two sales is noise.

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

Optional cap. Useful for a cheap trial run before a large pull.

## `concurrency` (type: `integer`):

Parallel department-year downloads. 4 is a good default.

## Actor input object example

```json
{
  "departments": [
    "75",
    "92"
  ],
  "years": [
    "2023",
    "2024"
  ],
  "communeCodes": [],
  "propertyTypes": [
    "Appartement",
    "Maison"
  ],
  "saleTypes": [
    "Vente"
  ],
  "excludeMultiLot": true,
  "minPrice": 200000,
  "maxPrice": 900000,
  "minSurface": 40,
  "outputFormat": "transactions",
  "minSalesPerGroup": 5,
  "maxResults": 100,
  "concurrency": 4
}
```

# Actor output Schema

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

One row per sale in transactions mode, or one row per commune x quarter x type in stats mode.

## `transactions` (type: `string`):

Sale-level view: date, price, price per m2, surface, rooms, address and coordinates.

## `marketStats` (type: `string`):

Median and quartile EUR/m2 per commune, quarter and property type.

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

Raw rows read, sales after deduplication, files fetched and any that failed.

# 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 = {
    "departments": [
        "75"
    ],
    "years": [
        "2024"
    ],
    "communeCodes": [],
    "propertyTypes": [
        "Appartement",
        "Maison"
    ],
    "saleTypes": [
        "Vente"
    ],
    "outputFormat": "transactions",
    "minSalesPerGroup": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("alinz/france-property-prices").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 = {
    "departments": ["75"],
    "years": ["2024"],
    "communeCodes": [],
    "propertyTypes": [
        "Appartement",
        "Maison",
    ],
    "saleTypes": ["Vente"],
    "outputFormat": "transactions",
    "minSalesPerGroup": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("alinz/france-property-prices").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 '{
  "departments": [
    "75"
  ],
  "years": [
    "2024"
  ],
  "communeCodes": [],
  "propertyTypes": [
    "Appartement",
    "Maison"
  ],
  "saleTypes": [
    "Vente"
  ],
  "outputFormat": "transactions",
  "minSalesPerGroup": 5
}' |
apify call alinz/france-property-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,alinz/france-property-prices"
        }
    }
}

```

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/wQDyUWbTKNb2J8p7E/builds/oq1mREHvn70wWOO0I/openapi.json
