# French DPE Leads Properties and Details Scraper (`realestateapi/french-dpe-leads-properties-and-details-scraper`) Actor

French DPE Leads Properties and Details Scraper — ADEME/DGFiP/SIRENE property lead collector; source requests currently remain policy-gated while Chrome cannot inspect the configured endpoint and current terms.

- **URL**: https://apify.com/realestateapi/french-dpe-leads-properties-and-details-scraper.md
- **Developed by:** [Real Estate API](https://apify.com/realestateapi) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## French DPE Leads Properties and Details Scraper

This Actor is prepared to match ADEME energy-performance certificates with legal-person property owners in the DGFiP archive, then optionally enrich matched SIRENs from the public SIRENE API. Live source requests are currently disabled: Chrome could not open the configured ADEME records endpoint, and the current ADEME usage-terms page was access denied. The default run therefore finishes as `SUCCEEDED` / `SKIPPED_POLICY` with zero requests and zero dataset rows.

### Source and data scope

The ADEME dataset page identifies the source as Open Licence 2.0, but its records endpoint could not be inspected in Chrome. The DGFiP join covers legal-person owner rows only; no match does not prove that a property has no owner. The Actor does not use alternate browsers, HTTP clients, proxies, indirect routes, or access-control workarounds to reach a blocked source.

The input schema describes bounded source, owner-join, SIRENE, and monitoring controls for a future reviewed run. While the policy gate in `src/config.js` is closed, these settings do not make source requests. The optional Slack digest runs only when monitoring is enabled and the caller supplies a webhook; the current policy skip makes no digest request.

### Dataset and run status

The dataset contains only validated source-backed property DPE rows: `lead` for a matched legal-person owner and `dpe-unmatched` when a valid DPE has no owner match. Operational diagnostics, empty-result placeholders, and error records are not dataset rows. Stage errors and request totals are summarized under `OUTPUT` and `RUN_METADATA`.

- `SUCCEEDED` / `COMPLETED`: collection finished without failures, including a clean empty result or reaching a configured item bound.
- `SUCCEEDED` / `PARTIAL`: at least one real property row was stored before a later source, dataset, or summary failure; the Actor exits successfully.
- `FAILED` / `FAILED`: one or more failures occurred and no real property row was stored; the Actor exits with code 1.
- `SUCCEEDED` / `SKIPPED_POLICY`: source review is not verified in Chrome, so the Actor makes no source requests and exits successfully.

`recordCount`, `successfulItems`, `failureCount`, and request counts in the summary reconcile with the active dataset. Its diagnostic row count remains zero.

### Local checks

```text
npm test
npm run check
npm run validate
apify validate-schema
```

The offline tests use synthetic ADEME/DGFiP/SIRENE responses. The default local Actor run exercises only the policy gate and makes no source request. These checks do not prove live extraction or current source access.

### Runtime storage and dependencies

The runtime uses Node.js HTTPS fetch and Apify’s documented default Dataset/KVS REST APIs. It no longer includes the unused Apify SDK/Crawlee HTTP caching stack. JSON input and records are supported; the platform provides the run token and default storage IDs through environment variables. Credentials are sent only in the Authorization header to api.apify.com. Storage redirects are refused. Dataset writes are not retried after uncertain failures, to prevent duplicates; read and idempotent summary writes may retry once. Local verification uses APIFY_LOCAL_STORAGE_DIR and preserves existing dataset rows. Source permission and access requirements still apply.

# Actor input Schema

## `limit` (type: `integer`):

Maximum distinct ADEME DPE records to inspect and emit.

## `city` (type: `string`):

Exact local city filter after ADEME retrieval.

## `postalCode` (type: `string`):

Optional exact five-digit postal-code filter; it also narrows the ADEME request.

## `department` (type: `string`):

Optional French department code such as 75, 2A, or 2B.

## `query` (type: `string`):

Optional ADEME full-text query; city is used when omitted.

## `dpeClasses` (type: `array`):

Energy classes requested from ADEME; the intended lead mode is F and G.

## `sourcePageSize` (type: `integer`):

Rows requested per ADEME page.

## `maxSourcePages` (type: `integer`):

Bound the local city filtering work when ADEME search relevance returns nearby names.

## `includeOwnerLookup` (type: `boolean`):

Download and scan the official year-specific DGFiP legal-person property archive for address matches.

## `ownerLegalForms` (type: `array`):

DGFiP abbreviated legal-form values to retain, for example SCI or SARL.

## `ownerDataYear` (type: `integer`):

Official DGFiP legal-person property archive year. The current default is 2025.

## `maxOwnerRowsScanned` (type: `integer`):

Safety bound on decoded DGFiP rows across department files.

## `maxOwnersPerDpe` (type: `integer`):

Maximum legal-person owner rows emitted for each DPE address.

## `stopAfterFirstOwner` (type: `boolean`):

Stop streaming a department file once every requested DPE has at least one legal-person owner match. Disable to collect up to maxOwnersPerDpe rows per DPE.

## `includeSirene` (type: `boolean`):

Look up matched nine-digit SIRENs in the public Recherche d'entreprises API.

## `maxSireneOwners` (type: `integer`):

Bounded number of distinct matched SIRENs to enrich.

## `includeRaw` (type: `boolean`):

Retain the complete ADEME source row under dpe.raw.

## `monitor` (type: `boolean`):

Compare the current lead snapshot with LAST_SNAPSHOT and classify new, updated, unchanged, and removed records.

## `slackWebhookUrl` (type: `string`):

Optional user-supplied Slack webhook used only when monitor is true; no webhook is called by default.

## `requestTimeoutSecs` (type: `integer`):

Timeout for ADEME and SIRENE requests.

## `archiveTimeoutSecs` (type: `integer`):

Timeout for downloading and scanning the official DGFiP archive.

## `maxDownloadBytes` (type: `integer`):

Reject an unexpectedly large archive before or during download.

## Actor input object example

```json
{
  "limit": 25,
  "city": "Paris",
  "dpeClasses": [
    "F",
    "G"
  ],
  "sourcePageSize": 100,
  "maxSourcePages": 10,
  "includeOwnerLookup": true,
  "ownerLegalForms": [
    "SCI",
    "SARL"
  ],
  "ownerDataYear": 2025,
  "maxOwnerRowsScanned": 2500000,
  "maxOwnersPerDpe": 3,
  "stopAfterFirstOwner": true,
  "includeSirene": true,
  "maxSireneOwners": 25,
  "includeRaw": false,
  "monitor": false,
  "requestTimeoutSecs": 45,
  "archiveTimeoutSecs": 900,
  "maxDownloadBytes": 260000000
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("realestateapi/french-dpe-leads-properties-and-details-scraper").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("realestateapi/french-dpe-leads-properties-and-details-scraper").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 realestateapi/french-dpe-leads-properties-and-details-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,realestateapi/french-dpe-leads-properties-and-details-scraper"
        }
    }
}
```

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/XQJLD2vpiG8Taru0P/builds/zgdcnsutycCgKo5XY/openapi.json
