# SeLoger & Leboncoin Listing Deal Checker – DVF Sold Prices (`kris_fili/seloger-leboncoin-deal-checker`) Actor

Check French property listings (SeLoger, Leboncoin, PAP, Bien'ici scrapers) against official DVF sale prices: fair value, % above or below, deal verdict and a report of the best deals.

- **URL**: https://apify.com/kris_fili/seloger-leboncoin-deal-checker.md
- **Developed by:** [Krzysztof Filipiak](https://apify.com/kris_fili) (community)
- **Categories:** Real estate, Integrations, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 listing checks

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

## SeLoger & Leboncoin Listing Deal Checker – compare asking prices with DVF sold prices

**Is this listing a bargain or overpriced?** Feed the property listings you scraped from **SeLoger, Leboncoin, PAP, Bien'ici, Logic-Immo** (or any other source) and get, for every apartment and house, its value according to **official sale prices from notarial deeds (DVF)** nearby – plus how many percent the asking price is above or below it and a clear verdict.

DVF (*Demandes de valeurs foncières*) is the register of property sales recorded by the French tax administration from notarial deeds. Listing portals only show asking prices; this Actor tells you what similar homes actually sold for.

### What you get for each listing

- **value from DVF sales** (€) and **price per m²**, with a **range**
- **asking vs sold value** in % (e.g. −8.2% = asking price 8.2% below what similar homes sold for)
- **typical premium** of the other listings from the same city in your input, and the listing's position against it
- a **verdict**: `great_deal`, `good_deal`, `typical`, `expensive` or `very_expensive` – and whether it is reliable
- **confidence**, number of comparable sales and what they were compared with (a radius around the listing, or its commune when the listing has no coordinates)
- the **3 most similar sales** (`similarSales`: date, price, area, rooms, price per m², street, distance)

Plus a **report** with the best deals and a table per city (Output tab → *Best deals*), and a JSON summary.

### How to use it with a scraper

1. Run any listings scraper from Apify Store – for example a SeLoger, Leboncoin or PAP scraper.
2. Open this Actor and pick that run's dataset in **Listings dataset**. Start.
3. Open **Best deals** in the Output tab.

**Automatically after every scrape:** on the scraper's **Integrations** tab click *Add integration* and choose this Actor – each finished scrape is checked without any copying.

Field names are recognised automatically: `price`, `prix`, `pricing.amount_eur`, `surface`, `square`, `property.surface_m2`, `rooms`, `pieces`, `real_estate_type`, `latitude`, `longitude`, `zipcode`, `city`, `url`… Unusual names can be mapped in `fieldMapping`, e.g. `{"price": "pricing.total", "area": "details.surface"}`. Listings with coordinates are compared with sales within 500 m (wider when there are too few); listings with only a postcode and city are compared with sales in their commune (Paris, Lyon and Marseille: their arrondissement).

### Example

Input (pasted listings – real run from October 2026):

```json
{
  "listings": [
    { "title": "Appartement 2 pièces 45 m²", "price": 445000, "surface": 45, "rooms": 2, "propertyType": "Appartement",
      "latitude": 48.8537, "longitude": 2.3709, "zipcode": "75011", "city": "Paris" },
    { "title": "Appartement 3 pièces 68 m²", "price": 395000, "surface": 68, "rooms": 3, "propertyType": "Appartement",
      "zipcode": "69003", "city": "Lyon" },
    { "title": "Maison 5 pièces 120 m²", "price": 520000, "surface": 120, "rooms": 5, "propertyType": "Maison",
      "zipcode": "33000", "city": "Bordeaux" }
  ]
}
```

Results:

| Listing | Asking €/m² | Sold value €/m² | Asking vs sold | Compared with |
|---|---|---|---|---|
| Paris 11e · apartment 45 m² · 445,000 € | 9,889 | 10,777 | −8.2% | 716 sales within 500 m |
| Lyon 3e · apartment 68 m² · 395,000 € | 5,809 | 4,502 | +29.0% | 3,256 sales in the arrondissement |
| Bordeaux · house 120 m² · 520,000 € | 4,333 | 4,831 | −10.3% | 2,567 sales in the commune |

One record (shortened):

```json
{
  "recordType": "listing",
  "listing": { "title": "Appartement 2 pièces 45 m²", "price": 445000, "areaM2": 45, "city": "Paris", "zip": "75011" },
  "askingPricePerM2Eur": 9889,
  "deedValueEur": 485000,
  "deedPricePerM2Eur": 10777,
  "deedValueRangeEur": { "low": 433000, "high": 544000 },
  "priceVsDeedValuePct": -8.2,
  "verdict": "good_deal",
  "confidence": "high",
  "comparablesUsed": 716,
  "comparisonArea": "500 m",
  "priceLevelAsOf": "2025-Q4",
  "similarSales": [
    { "date": "2025-10-07", "priceEur": 698000, "areaM2": 50, "rooms": 2, "pricePerM2Eur": 13960, "street": "Rue De La Roquette", "distanceM": 15 }
  ]
}
```

### How the verdict works

Asking prices are usually above final sale prices (negotiation, and DVF is published with a delay of about six months). So a listing is judged against the **typical premium of other listings from the same city** in your input (median of at least 12 listings):

| Listing vs typical premium | Verdict |
|---|---|
| 15 points or more below | `great_deal` |
| 7–15 points below | `good_deal` |
| within ±7 points | `typical` |
| 7–15 points above | `expensive` |
| 15 points or more above | `very_expensive` |

With fewer listings from a city, fixed bands on the raw difference are used (≤ −10% great deal, ≤ 0% good deal, < +15% typical, < +25% expensive). Verdicts based on few sales are marked `verdictReliable: false` and left out of *Best deals*.

### How the value is calculated

DVF sales of a single apartment or a single house (outbuildings allowed) from the last 36 months of data, adjusted to the newest quarter with the local quarterly trend and weighted by distance, living area, rooms and recency; the value is the weighted median price per m² × the listing's area. The same method as [France Property Valuation (DVF)](https://apify.com/kris_fili/france-property-valuation-dvf). It is an automated estimate, not a formal appraisal: it does not see the condition, floor, view or energy rating (DPE).

### Who uses it

- **Investors and *marchands de biens*** – screen thousands of listings and look only at the real bargains.
- **Real estate agents** – back up a price discussion with what similar homes actually sold for; spot overpriced private listings for prospecting.
- **Proptech and data teams** – enrich listing datasets with an official, sourced value per listing.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Listing check (only when a value is produced) | $0.01 |

Skipped listings – rentals, parking, plots, commercial premises, listings without a price, area or location, Alsace-Moselle – are **free** and listed with the reason. 1,000 checked listings cost $10.

### Data sources, coverage and privacy

- **DVF** (Demandes de valeurs foncières), DGFiP, geolocated by Etalab – Licence Ouverte 2.0; locations from IGN Géoplateforme.
- Not covered by DVF: Alsace-Moselle (departments 57, 67, 68) and Mayotte.
- DVF has no names of buyers or sellers. Your listings are processed only in your run; results are not published or indexed.

### Related Actors

- [France Property Valuation & Sold Prices (DVF)](https://apify.com/kris_fili/france-property-valuation-dvf) – a detailed valuation of one address, or a market report for any commune.
- [Otodom & OLX Listing Deal Checker](https://apify.com/kris_fili/otodom-listing-deal-checker) – the same for Poland, on notarial deeds from the RCN register.

***

#### En français – cette annonce est-elle une bonne affaire ?

Comparez les annonces immobilières **SeLoger, Leboncoin, PAP, Bien'ici** (par exemple le résultat d'un scraper Apify) avec les **prix de vente réels DVF** : pour chaque appartement ou maison, la valeur estimée à partir des ventes comparables à proximité, l'écart du prix affiché en %, et un verdict – **bonne affaire, prix normal ou trop cher** – par rapport aux autres annonces de la même ville. Un rapport liste les meilleures affaires. 0,01 $ par annonce analysée ; les annonces ignorées (location, parking, terrain, données manquantes) sont gratuites.

# Actor input Schema

## `datasetId` (type: `string`):

Pick the dataset of a listings scraper run – SeLoger, Leboncoin, PAP, Bien'ici, Logic-Immo or any scraper whose items have a price, living area and coordinates or postcode/city. When this Actor runs as an integration after a scraper, the dataset is passed automatically.

## `listings` (type: `array`):

Used only when no dataset is selected. Each item needs a price (€), living area and coordinates (latitude/longitude) or a postcode and city. Field names are recognised automatically (price, prix, surface, square, rooms, pieces, latitude, longitude, zipcode, city, url…).

## `maxListings` (type: `integer`):

Stop after this many listings from the dataset (1–20,000). Each checked listing is charged; skipped ones (rentals, parking, missing data) are free.

## `monthsBack` (type: `integer`):

How many months of sales to compare with (12–60), counted back from the newest DVF data. Older sales are adjusted with the local trend.

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

Date, price, area, street and distance of the most similar DVF sales (no extra charge).

## `includeSkipped` (type: `boolean`):

Rentals, parking, plots, listings without price or area… are written to the dataset as 'skipped' (free).

## `fieldMapping` (type: `object`):

Only if your scraper uses unusual field names. Dotted paths are allowed, e.g. {"price": "pricing.amount_eur", "area": "property.surface_m2", "zip": "location.zipcode"}. Keys: price, area, rooms, lat, lon, zip, city, district, type, market, url, id, title.

## Actor input object example

```json
{
  "listings": [
    {
      "title": "Appartement 2 pièces 45 m²",
      "price": 445000,
      "surface": 45,
      "rooms": 2,
      "propertyType": "Appartement",
      "latitude": 48.8537,
      "longitude": 2.3709,
      "zipcode": "75011",
      "city": "Paris",
      "url": "https://example.com/annonce-1"
    },
    {
      "title": "Appartement 3 pièces 68 m²",
      "price": 395000,
      "surface": 68,
      "rooms": 3,
      "propertyType": "Appartement",
      "zipcode": "69003",
      "city": "Lyon",
      "url": "https://example.com/annonce-2"
    },
    {
      "title": "Maison 5 pièces 120 m²",
      "price": 520000,
      "surface": 120,
      "rooms": 5,
      "propertyType": "Maison",
      "zipcode": "33000",
      "city": "Bordeaux",
      "url": "https://example.com/annonce-3"
    }
  ],
  "maxListings": 1000,
  "monthsBack": 36,
  "includeComparables": true,
  "includeSkipped": true,
  "fieldMapping": {}
}
```

# Actor output Schema

## `report` (type: `string`):

No description

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

No description

## `summary` (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 = {
    "listings": [
        {
            "title": "Appartement 2 pièces 45 m²",
            "price": 445000,
            "surface": 45,
            "rooms": 2,
            "propertyType": "Appartement",
            "latitude": 48.8537,
            "longitude": 2.3709,
            "zipcode": "75011",
            "city": "Paris",
            "url": "https://example.com/annonce-1"
        },
        {
            "title": "Appartement 3 pièces 68 m²",
            "price": 395000,
            "surface": 68,
            "rooms": 3,
            "propertyType": "Appartement",
            "zipcode": "69003",
            "city": "Lyon",
            "url": "https://example.com/annonce-2"
        },
        {
            "title": "Maison 5 pièces 120 m²",
            "price": 520000,
            "surface": 120,
            "rooms": 5,
            "propertyType": "Maison",
            "zipcode": "33000",
            "city": "Bordeaux",
            "url": "https://example.com/annonce-3"
        }
    ],
    "fieldMapping": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("kris_fili/seloger-leboncoin-deal-checker").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 = {
    "listings": [
        {
            "title": "Appartement 2 pièces 45 m²",
            "price": 445000,
            "surface": 45,
            "rooms": 2,
            "propertyType": "Appartement",
            "latitude": 48.8537,
            "longitude": 2.3709,
            "zipcode": "75011",
            "city": "Paris",
            "url": "https://example.com/annonce-1",
        },
        {
            "title": "Appartement 3 pièces 68 m²",
            "price": 395000,
            "surface": 68,
            "rooms": 3,
            "propertyType": "Appartement",
            "zipcode": "69003",
            "city": "Lyon",
            "url": "https://example.com/annonce-2",
        },
        {
            "title": "Maison 5 pièces 120 m²",
            "price": 520000,
            "surface": 120,
            "rooms": 5,
            "propertyType": "Maison",
            "zipcode": "33000",
            "city": "Bordeaux",
            "url": "https://example.com/annonce-3",
        },
    ],
    "fieldMapping": {},
}

# Run the Actor and wait for it to finish
run = client.actor("kris_fili/seloger-leboncoin-deal-checker").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 '{
  "listings": [
    {
      "title": "Appartement 2 pièces 45 m²",
      "price": 445000,
      "surface": 45,
      "rooms": 2,
      "propertyType": "Appartement",
      "latitude": 48.8537,
      "longitude": 2.3709,
      "zipcode": "75011",
      "city": "Paris",
      "url": "https://example.com/annonce-1"
    },
    {
      "title": "Appartement 3 pièces 68 m²",
      "price": 395000,
      "surface": 68,
      "rooms": 3,
      "propertyType": "Appartement",
      "zipcode": "69003",
      "city": "Lyon",
      "url": "https://example.com/annonce-2"
    },
    {
      "title": "Maison 5 pièces 120 m²",
      "price": 520000,
      "surface": 120,
      "rooms": 5,
      "propertyType": "Maison",
      "zipcode": "33000",
      "city": "Bordeaux",
      "url": "https://example.com/annonce-3"
    }
  ],
  "fieldMapping": {}
}' |
apify call kris_fili/seloger-leboncoin-deal-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kris_fili/seloger-leboncoin-deal-checker"
        }
    }
}
```

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/R7hA6nWV5GxHZ8Eoy/builds/TZXGMyl8TypG8gydB/openapi.json
