# France Price per m² by Address – Batch DVF Comps (`characteristic_division/dvf-price-per-m2-batch`) Actor

Paste a list of French addresses and get the median, quartiles and yearly trend of price per m² from real notarised sales (official DVF open data) around each one, for flats and houses, plus an optional value range for a given surface. One row per address, $0.01 each.

- **URL**: https://apify.com/characteristic_division/dvf-price-per-m2-batch.md
- **Developed by:** [Jordan Banchonpanith](https://apify.com/characteristic_division) (community)
- **Categories:** Real estate, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 address reports

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

## France Price per m² by Address – Batch DVF Comps (Prix m² DVF par adresse)

**France Price per m² by Address** takes a list of **French addresses** (or coordinates) and returns, for each one, the **price per m² of real recorded property sales nearby**, computed from the official **DVF** open data (*Demandes de valeurs foncières*, DGFiP / Etalab). One address in, one row out: **median €/m²**, price range, yearly trend and an optional value range for a given surface.

It is built for portfolio screening, valuation sanity checks, lead scoring, relocation tools and spreadsheets that need DVF prices for many addresses at once.

### What you get for each address

- **median, P25, P75, mean, min and max €/m²**, plus median price and median surface, separately for **flats (appartements)** and **houses (maisons)**
- a **year-by-year median** and the **trend** in % from the first year to the last
- an optional **value range** (low / mid / high) for a given surface, using only sales of a similar size (±30% by default)
- an optional **comparables list** of the nearest sales: date, price, surface, rooms, €/m², distance and street name

### How to get DVF price per m² for a list of addresses

1. Paste addresses into **Addresses**, one per line, or as objects with a property type and surface.
2. Keep the defaults (500 m radius, at least 10 sales, last 3 DVF years) or adjust them.
3. Run, then export the table to CSV / Excel / JSON or call the Actor from the Apify API.

```json
{
  "addresses": [
    "10 rue de la Paix, Paris",
    { "address": "12 rue Sainte-Catherine, Bordeaux", "propertyType": "Appartement", "surface": 55, "label": "flat-bdx" }
  ],
  "radiusMeters": 500,
  "minComparables": 10,
  "maxRadiusMeters": 2000,
  "yearsBack": 3,
  "includeComparables": false
}
```

| Field | Description |
|---|---|
| `addresses` | Strings, or objects `{address, propertyType, surface, rooms, label}` (max 5,000 per run) |
| `points` | Already geocoded `{lat, lon, label, …}` |
| `propertyTypes` | `Appartement` and/or `Maison` (default both) |
| `radiusMeters` / `minComparables` / `maxRadiusMeters` | Adaptive radius: 500 m, at least 10 sales, up to 2,000 m (defaults) |
| `yearsBack` or `years` | Defaults to the 3 most recent published DVF years (auto-detected, currently 2023–2025) |
| `surfaceTolerancePct` | Similar-size filter used when an address gives a surface (default ±30%) |
| `includeVefa` | Include off-plan new-builds (VEFA) (default `false`) |
| `includeComparables`, `maxComparables` | Optional comparables list (default off, max 50 per type) |
| `minGeocodeScore` | Reject weak address matches (default 0.5). Rejected addresses are not charged. |

### How the price per m² is calculated

- **Clean sales only.** Only sales of exactly one dwelling are kept (outbuildings such as cellars or parking are allowed). Multi-unit sales, sales mixed with commercial premises, exchanges and symbolic prices are dropped, because their €/m² is meaningless.
- **Adaptive radius.** The search starts at 500 m and doubles until at least `minComparables` sales are found, up to `maxRadiusMeters`, so rural addresses still get an answer.
- **Paris, Lyon and Marseille** are resolved to their arrondissements, which is how DVF files are keyed. Neighbouring communes inside the radius are included automatically.
- **Sources:** DVF géolocalisées files on `files.data.gouv.fr` (Etalab, Licence Ouverte 2.0); geocoding with the national address base (BAN) via the IGN Géoplateforme; commune lookup via `geo.api.gouv.fr`. No scraping, no login, no API key.

### Output example (one row per address)

From a real run on 2026-11-05 (trimmed). The Output tab has an "Overview" table; every field is described in the dataset schema.

```json
{
  "input": "12 rue Sainte-Catherine, Bordeaux", "label": "flat-bdx", "found": true,
  "geocodedLabel": "12 Rue Sainte-Catherine 33000 Bordeaux", "geocodeScore": 0.99,
  "communeCode": "33063", "communeName": "Bordeaux", "years": [2023, 2024, 2025], "radiusMetersUsed": 500,
  "subject": { "propertyType": "Appartement", "surface": 55, "rooms": null },
  "apartmentMedianPricePerM2": 4827, "apartmentSales": 266,
  "apartment": {
    "count": 266, "medianPricePerM2": 4827, "p25PricePerM2": 4184, "p75PricePerM2": 5681,
    "medianPrice": 253690, "medianSurface": 53,
    "perYear": { "2023": { "count": 96, "medianPricePerM2": 5126 }, "2024": { "count": 70, "medianPricePerM2": 4818 }, "2025": { "count": 100, "medianPricePerM2": 4734 } },
    "trendFirstToLastYearPct": -7.6,
    "estimate": { "surface": 55, "low": 230120, "mid": 265485, "high": 312455, "confidence": "high" },
    "comparables": [{ "date": "2024-10-01", "type": "Appartement", "price": 301000, "surface": 56, "rooms": 1, "pricePerM2": 5375, "distanceM": 20, "street": "RUE SAINTE CATHERINE", "postcode": "33000", "commune": "Bordeaux" }]
  }
}
```

Addresses that fail return `found: false` and an `error`, for example "Address not found by the national address base (BAN)" or "DVF does not cover Bas-Rhin…". **They are not charged.**

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| `address-report` | **$0.01** | An address with at least one clean comparable sale |
| Actor start | $0.001 | Once per run |
| Not found, low geocoding score, outside DVF coverage, no sales | free | |

1,000 addresses cost about $10. Set a maximum cost per run and the Actor stops cleanly when it is reached.

### Privacy and DVF reuse conditions

DVF contains no names, but article R\*112 A-3 of the *Livre des procédures fiscales* forbids any reuse that could **re-identify** people and any **search-engine indexing** of the data. This Actor therefore:

- outputs **aggregates by default**
- in the optional comparables list, **never outputs house numbers, cadastral parcel IDs or exact coordinates** (street name, distance rounded to 10 m, and commune only)
- never joins DVF with any other dataset

You are responsible for using the results in line with these conditions. In particular, do **not** publish comparables on publicly indexable web pages. Apify datasets are private by default.

### Limits

- DVF covers sales recorded by notaries, with about 6 months of lag; Etalab updates the files twice a year. Prices are net seller prices, as recorded.
- The estimate is a **comparables-based range, not a professional appraisal**: it ignores floor, condition, view and energy rating. `confidence` reflects the number of sales (high ≥ 30, medium ≥ 10).
- Not covered: Alsace-Moselle (57, 67, 68) and Mayotte.
- Speed: about 5–10 s for the first address in a new area, well under 1 s for nearby addresses (files are cached per run).

### FAQ

**Is this an official DVF API?** It uses the official DVF open-data files and the official address API, and adds the aggregation (median €/m², trend, estimate). It is not operated by the DGFiP or Etalab.

**Can I get prices for a whole street or district?** Yes: pass one address in that street or district, or several points, and set the radius.

**Does it work for new-builds?** Off-plan sales (VEFA) are excluded by default; set `includeVefa: true` to include them.

### En français : prix au m² DVF par adresse, en lot

Collez une liste d'**adresses en France** : pour chacune, l'Actor renvoie le **prix au m² médian** des **ventes réelles DVF** autour de l'adresse (appartements et maisons séparés), les quartiles P25/P75, l'**évolution par année** et, si vous indiquez une surface, une **fourchette d'estimation** basée sur des biens de taille comparable. Données officielles DVF géolocalisées (DGFiP / Etalab), géocodage BAN. Rayon adaptatif de 500 m à 2 km, Paris/Lyon/Marseille par arrondissement. Hors couverture : Alsace-Moselle et Mayotte. Prix : 0,01 $ par adresse ; les adresses introuvables ne sont pas facturées. Ce n'est pas une expertise immobilière.

### Support

This Actor is **maintained by an AI agent on behalf of its owner**. Report problems in the Issues tab; **issues are fixed within 14 days**.

Data: DVF géolocalisées © DGFiP / Etalab, Licence Ouverte 2.0. BAN © IGN / Etalab. Découpage administratif © Etalab / INSEE. Not affiliated with the DGFiP or Etalab.

### Ready-to-run example tasks

- [DVF prix m² par adresse – Paris (price per m²)](https://apify.com/characteristic_division/dvf-price-per-m2-batch/examples/dvf-prix-m2-par-adresse-paris)
- [Estimate a French flat's value range from DVF comparables](https://apify.com/characteristic_division/dvf-price-per-m2-batch/examples/estimate-flat-value-from-dvf-comps)
- [Compare price per m² across French city centres (DVF)](https://apify.com/characteristic_division/dvf-price-per-m2-batch/examples/compare-price-per-m2-french-cities)

### More Actors from the same maintainer

- [France Company Health Check by SIREN – BODACC Batch](https://apify.com/characteristic_division/bodacc-siren-health-check): insolvency, strike-off and late-accounts signals for French companies
- [Swiss VAT (MWST/TVA) & UID Validator](https://apify.com/characteristic_division/swiss-uid-vat-validator): bulk check of Swiss VAT / UID numbers in the official register
- [Google Sheets Import/Export – Service Account](https://apify.com/characteristic_division/sheets-sync-service-account): send these results to a Google Sheet without OAuth expiry

# Actor input Schema

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

French addresses, one per line (max 5,000). For a value estimate, use objects instead: {"address": "12 rue Sainte-Catherine, Bordeaux", "propertyType": "Appartement", "surface": 55, "rooms": 2, "label": "my-id"}.

## `points` (type: `array`):

Already geocoded points: \[{"lat": 48.869, "lon": 2.331, "label": "site A"}]. Same optional fields as addresses (propertyType, surface, rooms).

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

Which DVF dwelling types to aggregate when an address does not set its own propertyType.

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

Initial radius around each address.

## `minComparables` (type: `integer`):

If fewer sales are found, the radius doubles until this number is reached or the maximum radius is hit.

## `maxRadiusMeters` (type: `integer`):

Upper bound for the auto-expanding radius.

## `yearsBack` (type: `integer`):

Use the N most recent DVF years published (auto-detected). Ignored when 'years' is set.

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

E.g. \[2023, 2024]. Leave empty to use the latest published years.

## `surfaceTolerancePct` (type: `integer`):

When an address gives propertyType + surface, only sales within ±this % of that surface are used.

## `includeVefa` (type: `boolean`):

Off-plan sales are priced higher than resale; excluded by default.

## `includeComparables` (type: `boolean`):

Adds the nearest sales (date, price, surface, rooms, €/m², distance, street name). House numbers, parcel IDs and exact coordinates are never output.

## `maxComparables` (type: `integer`):

Only used when the comparables list is enabled.

## `minGeocodeScore` (type: `number`):

Addresses matched by the national address base (BAN) below this score (0–1) are rejected and not charged.

## Actor input object example

```json
{
  "addresses": [
    "10 rue de la Paix, Paris",
    "12 rue Sainte-Catherine, Bordeaux",
    {
      "address": "1 place Bellecour, Lyon",
      "propertyType": "Appartement",
      "surface": 70,
      "label": "flat-42"
    }
  ],
  "propertyTypes": [
    "Appartement",
    "Maison"
  ],
  "radiusMeters": 500,
  "minComparables": 10,
  "maxRadiusMeters": 2000,
  "yearsBack": 3,
  "surfaceTolerancePct": 30,
  "includeVefa": false,
  "includeComparables": false,
  "maxComparables": 10,
  "minGeocodeScore": 0.5
}
```

# Actor output Schema

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

One row per input item; field definitions in the dataset schema (overview view).

## `runSummary` (type: `string`):

Counts of charged, free and failed items and API requests for this run.

# 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 = {
    "addresses": [
        "10 rue de la Paix, Paris",
        "12 rue Sainte-Catherine, Bordeaux",
        {
            "address": "1 place Bellecour, Lyon",
            "propertyType": "Appartement",
            "surface": 70,
            "label": "flat-42"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("characteristic_division/dvf-price-per-m2-batch").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 = { "addresses": [
        "10 rue de la Paix, Paris",
        "12 rue Sainte-Catherine, Bordeaux",
        {
            "address": "1 place Bellecour, Lyon",
            "propertyType": "Appartement",
            "surface": 70,
            "label": "flat-42",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("characteristic_division/dvf-price-per-m2-batch").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 '{
  "addresses": [
    "10 rue de la Paix, Paris",
    "12 rue Sainte-Catherine, Bordeaux",
    {
      "address": "1 place Bellecour, Lyon",
      "propertyType": "Appartement",
      "surface": 70,
      "label": "flat-42"
    }
  ]
}' |
apify call characteristic_division/dvf-price-per-m2-batch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,characteristic_division/dvf-price-per-m2-batch"
        }
    }
}
```

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/YHSRlE2kOSbOphRmA/builds/oUn4FOvKGDWR6MF4F/openapi.json
