# French Buildings API — RNB Building ID, Address, Parcel (`mouadapi/france-buildings`) Actor

Returns French buildings from the national register (RNB) by address, parcel, point or ID: ID-RNB, status, footprint, addresses, parcels, BD TOPO/BDNB ids; failed and unchanged rows are never charged. Not affiliated with or endorsed by the RNB, IGN or the French government.

- **URL**: https://apify.com/mouadapi/france-buildings.md
- **Developed by:** [COMPASS DEV](https://apify.com/mouadapi) (community)
- **Categories:** Real estate, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 building returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Returns French building records from the national building register (RNB) — ID-RNB, status, location, footprint,
addresses, cadastral parcels and BD TOPO / BDNB ids — by ID-RNB, address, cadastral parcel, point or commune, and is
never charged for failed, no-data or unchanged rows.

Give it ID-RNB building ids, French postal addresses, cadastral parcel ids, points (latitude,longitude) or commune INSEE
codes; get one flat row per building from the RNB (Référentiel National des Bâtiments), France's public register that
gives every building a stable identifier. Built for property, energy and insurance data work, spreadsheets, GIS and AI
agents that need "which building is this, and what are its ids?" without handling the API's paging, geocoding or formats.
*Not affiliated with or endorsed by the RNB, IGN or the French government.*

### What it does

- **Address → building:** the address is matched in the BAN (France's national address base) and the buildings at that
  address are returned, with the match score.
- **Parcel → buildings:** the buildings on a cadastral parcel, largest share first.
- **Point → buildings:** the buildings within a radius of a latitude,longitude point, nearest first, with their distance.
- **ID-RNB → building:** the full record of a known building; **BAN key → buildings:** the buildings linked to an address
  key; **commune → buildings:** a commune's buildings by INSEE code.
- Every building row carries its ID-RNB, status (constructed, notUsable, demolished), point, footprint as GeoJSON, its
  addresses and BAN keys, its ids in IGN's BD TOPO and in the BDNB and, on request, its cadastral parcels.
- **Watch mode** (give a watch list name in `stateName`): remembers every building and returns only:
  - `baseline`: the first run of the list;
  - `new`: a building the list did not have (for a commune: a building added to the register);
  - `changed`: a tracked field changed (status, addresses, footprint, ids or parcels; `changedFields` and
    `previousValues` say what and from what);
  - unchanged buildings are free and left out (`includeUnchanged: true` returns them as free rows).
  - A watch run with nothing new or changed returns **exactly one free `no_data` row** that says so.
- **No personal data:** the RNB holds buildings, not owners. The users who validated a building (`validated_by` in the
  API) are never read into a row.
- **You are never charged for failed results:** failed, no_data and unchanged rows are free.

### Quick start

```json
{ "queries": ["9QX7W3BF6RP6", "11 Allee du Garde, 33360 Cénac", "33118000AH0084"] }
```

A point (nearest buildings within 30 m) and a commune's buildings, with their cadastral parcels:

```json
{ "queries": ["44.7925,-0.4470"], "inseeCodes": ["33118"], "withPlots": true, "maxBuildingsPerQuery": 50 }
```

Watch a portfolio of buildings for status, address or footprint changes (the first run is the baseline):

```json
{ "queries": ["9QX7W3BF6RP6", "ZVZJBSDMATWR"], "stateName": "my-buildings" }
```

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `queries` | list of strings | — (prefilled with an ID-RNB, an address and a parcel) | One entry per line: an ID-RNB (12 characters), a French postal address, a cadastral parcel id (14 characters), `latitude,longitude`, or a BAN address key. Never a URL. A 5-digit code alone is refused (postal code or INSEE code?): use `inseeCodes` |
| `inseeCodes` | list of strings | — | Communes by INSEE code (not postal code), e.g. `33118`, `2A004` |
| `withPlots` | boolean | `false` | Also return the cadastral parcels each building intersects (ID-RNB, BAN key and commune entries) |
| `radiusMeters` | integer 0–1,000 | `30` | Point entries: the search radius |
| `minScore` | number 0–1 | `0.8` | Address entries: the lowest BAN match score (the RNB's own default) |
| `maxBuildingsPerQuery` | integer 1–10,000 | `100` | Most buildings returned for one entry |
| `mode` | `watch` or `export` | — | Empty: watch when `stateName` is given, otherwise export. An explicit mode wins |
| `stateName` | string | — | Watch list name. Watch mode without a name uses the list `default` |
| `includeUnchanged` | boolean | `false` | Watch: also return unchanged buildings (free) |
| `maxItems` | integer 1–100,000 | `1000` | Most charged buildings per run; in watch mode the rest come in the next run |

### Output

```json
{
    "status": "ok",
    "attempts": 1,
    "error": null,
    "input": "16 Avenue de Mons, 33360 Cénac",
    "mode": "export",
    "watchList": null,
    "dropReason": null,
    "queryType": "address",
    "rnbId": "9QX7W3BF6RP6",
    "buildingStatus": "constructed",
    "isActive": true,
    "latitude": 44.79249603125738,
    "longitude": -0.446996046489425,
    "shapeType": "MultiPolygon",
    "shapeGeoJson": "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-0.4471,44.7924],[-0.4469,44.7925],[-0.4468,44.7924],[-0.4471,44.7924]]]]}",
    "addresses": ["16 Avenue de Mons, 33360 Cénac"],
    "banKeys": ["33118_4999_00016"],
    "streetNumber": "16",
    "street": "Avenue de Mons",
    "postalCode": "33360",
    "city": "Cénac",
    "inseeCode": "33118",
    "bdtopoIds": ["BATIMENT0000000255605144"],
    "bdnbIds": ["bdnb-bc-51DW-XJQH-TUAX"],
    "parcels": null,
    "mainParcelId": null,
    "distanceMeters": null,
    "banScore": 0.9612,
    "matchedBanKey": "33118_4999_00016",
    "parcelCoverRatio": null,
    "changeType": null,
    "changedFields": null,
    "previousValues": null,
    "url": "https://rnb.beta.gouv.fr/carte?q=9QX7W3BF6RP6",
    "source": "Référentiel National des Bâtiments (RNB), rnb-api.beta.gouv.fr. Licence Ouverte 2.0 (Etalab): source RNB, retrieved on the scrapedAt date.",
    "license": "Licence Ouverte / Open Licence 2.0 (Etalab)",
    "licenseUrl": "https://www.etalab.gouv.fr/licence-ouverte-open-licence/",
    "scrapedAt": "2026-10-03T10:00:00.000Z"
}
```

| Status | Meaning | Charged? |
|---|---|---|
| `ok` | A building returned | Export: yes. Watch: `baseline`, `new` and `changed` yes; `unchanged` no |
| `no_data` | No building for the entry (an unknown ID-RNB, an address the BAN didn't match closely, an empty parcel or point), or nothing new since the last run (`error` says which) | No |
| `failed` | Invalid entry, or no answer after retries (`error` says why) | No |

The key-value store holds `RUN_REPORT` (counts, charged and free rows, requests, stop reason), `PROGRESS` (the export
resume list) and, when something fails, the raw response (`SNAPSHOT_*`, validators' usernames removed).

### Use it from AI agents

One clear main input, `queries`; every row has `status`, `error`, `rnbId`, `url` and `scrapedAt`. Call it through the
Apify API, the Apify MCP server (`mouadapi/france-buildings`) or x402 agentic payments. Copy-paste call (your Apify token
in `APIFY_TOKEN`):

```bash
curl -s -X POST "https://api.apify.com/v2/acts/mouadapi~france-buildings/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["9QX7W3BF6RP6", "16 Avenue de Mons, 33360 Cénac"]}'
```

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('mouadapi/france-buildings').call({ queries: ['9QX7W3BF6RP6', '16 Avenue de Mons, 33360 Cénac'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Output fields

| Field | Type | Description |
|---|---|---|
| `status` | string (or null) | ok = a building returned (charged in export mode and for baseline, new and changed rows; free when unchanged); no_data = no building for the entry, or nothing new since the last run (free); failed = invalid entry or no answer (free) |
| `attempts` | integer (or null) | Requests made for this entry's page (retries included) |
| `error` | string (or null) | Why a row is no_data or failed; null on ok rows |
| `input` | string (or null) | The entry that produced this row (an ID-RNB, an address, a parcel, a point, a BAN key or a commune code) |
| `mode` | string (or null) | watch or export |
| `watchList` | string (or null) | Watch list name (watch mode); null in export mode |
| `dropReason` | string (or null) | Kept for the shared row format; always null here (no record of this source is a person) |
| `queryType` | string (or null) | How the entry was read: rnb_id, address, parcel, point, ban_key or commune |
| `rnbId` | string (or null) | The building's national identifier in the RNB (12 characters, stable for the building's whole life) |
| `buildingStatus` | string (or null) | constructed, notUsable or demolished (the RNB's status of the building) |
| `isActive` | boolean (or null) | true when the ID-RNB is a real building (a demolished building stays active); false when the ID was deactivated as not a building |
| `latitude` | number (or null) | The building's point, WGS 84 (EPSG:4326) |
| `longitude` | number (or null) | The building's point, WGS 84 (EPSG:4326) |
| `shapeType` | string (or null) | The footprint's GeoJSON type: Polygon, MultiPolygon or Point |
| `shapeGeoJson` | string (or null) | The RNB's best known outline of the building, as GeoJSON text (WGS 84); not a reference footprint |
| `addresses` | array (or null) | The building's addresses from the BAN (France's national address base), as text |
| `banKeys` | array (or null) | The BAN interoperability key of each address (the same order as addresses) |
| `streetNumber` | string (or null) | First address: the number, with its repetition index (bis, ter…) when there is one |
| `street` | string (or null) | First address: the street |
| `postalCode` | string (or null) | First address: the postal code |
| `city` | string (or null) | First address: the commune |
| `inseeCode` | string (or null) | First address: the commune's INSEE code |
| `bdtopoIds` | array (or null) | The building's ids in IGN's BD TOPO |
| `bdnbIds` | array (or null) | The building's ids in the BDNB (national building database) |
| `parcels` | array (or null) | With "Include cadastral parcels": the parcels the building intersects and the share of the building on each (a geometric link, not a fiscal one); null otherwise |
| `mainParcelId` | string (or null) | With "Include cadastral parcels": the parcel holding the largest share of the building |
| `distanceMeters` | number (or null) | Point entries: the building's distance from the point, in metres |
| `banScore` | number (or null) | Address entries: the BAN geocoding score of the address matched (0 to 1) |
| `matchedBanKey` | string (or null) | Address entries: the BAN address key the address matched |
| `parcelCoverRatio` | number (or null) | Parcel entries: the share of the building on the parcel (0 to 1); buildings come largest share first |
| `changeType` | string (or null) | Watch mode: baseline (first run of the list), new, changed or unchanged (free); null in export mode |
| `changedFields` | string (or null) | Watch mode: tracked fields that changed, comma-separated |
| `previousValues` | string (or null) | Watch mode: the changed fields' previous values, as JSON |
| `url` | string (or null) | The building on the RNB's public map |
| `source` | string (or null) | Source and attribution (Licence Ouverte 2.0 asks reusers to name the source and the date) |
| `license` | string (or null) | Licence of the data |
| `licenseUrl` | string (or null) | Licence URL |
| `scrapedAt` | string (or null) | When the row was made (ISO 8601); the date to cite with the source |

### Pricing

Pay per event: one `building` event per returned building (export: every building; watch: `baseline`, `new` and
`changed` rows). `no_data`, `failed` and unchanged rows are free.

| Plan | Price per 1,000 buildings |
|---|---|
| Free | $1.50 |
| Bronze | $1.30 |
| Silver | $1.15 |
| Gold (and higher) | $1.00 |

Apify also charges its small per-run start event. There are no usage fees on top.

### Limits

- One request at a time, about 3 a second at most (the RNB publishes a limit of 20 requests a second per IP address). If
  the RNB answers HTTP 429, the run pauses 60, 120 and 240 seconds on the same connection, then stops with free `failed`
  rows. No other IP or proxy is ever tried.
- At most 1,000 entries per run and 10,000 buildings per entry. For millions of buildings, the RNB's national and
  departmental export files on data.gouv.fr suit better.

### Known issues

- The RNB API is in alpha: its formats may change. The daily canary checks this Actor against the live API.
- A parcel's buildings come from a geometric intersection, not a fiscal link: a building that overlaps two parcels is
  returned for both, with its share on each.
- An address that the BAN matches with a score under `minScore` returns a free `no_data` row with the score.

### FAQ

**Is this the official RNB API?** No: it is an Apify Actor that calls the RNB's public API and returns its records as
flat rows. *Not affiliated with or endorsed by the RNB, IGN or the French government.*

**Are owners' names returned?** No. The RNB holds buildings and addresses, not owners, and the users who validated a
building are never read into a row.

**Why was my 5-digit code refused?** It can be a postal code or a commune's INSEE code (Cénac is 33360 by post and 33118
by INSEE). Put an INSEE code in `inseeCodes`, or write the full address in `queries`.

### Data and licence

- Source: the API of the Référentiel National des Bâtiments (RNB), documented at
  https://rnb-fr.gitbook.io/documentation/api-et-outils/api-batiments.
- Licence: Licence Ouverte / Open Licence 2.0 (Etalab), as declared on data.gouv.fr's "API RNB" record
  (https://www.etalab.gouv.fr/licence-ouverte-open-licence/). Reuse, including commercial reuse, is allowed if you name
  the source and the date: every row carries the source and the retrieval date (`scrapedAt`).
- This Actor is not affiliated with, endorsed by or provided by the RNB, IGN or the French government. It returns the data
  as published.

# Actor input Schema

## `queries` (type: `array`):

One entry per line: an ID-RNB (12 characters, for example 9QX7W3BF6RP6), a French postal address (for example 16 Avenue de Mons, 33360 Cénac), a cadastral parcel id (14 characters, for example 33118000AD0017), a point as latitude,longitude (for example 44.7925,-0.4470) or a BAN address key (for example 33118_4999_00016). Never a URL. A 5-digit code alone is refused (it can be a postal code or an INSEE code): use inseeCodes for a commune.

## `inseeCodes` (type: `array`):

Communes whose buildings to list, by INSEE code (not the postal code; for example 33118 for Cénac, 2A004 for Ajaccio). Each commune returns up to "Max buildings per entry" buildings.

## `withPlots` (type: `boolean`):

Also return the cadastral parcels each building intersects, with the share of the building on each (for ID-RNB, BAN key and commune entries). A geometric link, not a fiscal one.

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

For latitude,longitude entries: the buildings within this distance of the point, nearest first.

## `minScore` (type: `number`):

For address entries: the lowest score the address must reach in the BAN (France's national address base). Under it the entry is a free no-data row. The RNB's own default is 0.8.

## `maxBuildingsPerQuery` (type: `integer`):

Most buildings returned for one entry (an address, a point, a parcel or a commune can hold many).

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

Empty: watch when a watch list name is given, otherwise export. "watch" returns only buildings that are new or changed (status, addresses, footprint, ids or parcels) since the last run of the watch list; "export" returns every building. An explicit mode wins.

## `stateName` (type: `string`):

Name of your building watch list (kept in your own storage between runs). Giving a name turns on watch mode. Watch mode without a name uses the list "default".

## `includeUnchanged` (type: `boolean`):

Watch mode: also return buildings that did not change, as free rows.

## `maxItems` (type: `integer`):

Most buildings returned and charged per run. In watch mode the rest come in the next run.

## Actor input object example

```json
{
  "queries": [
    "9QX7W3BF6RP6",
    "11 Allee du Garde, 33360 Cénac",
    "33118000AH0084"
  ],
  "inseeCodes": [
    "33118"
  ],
  "withPlots": false,
  "radiusMeters": 30,
  "minScore": 0.8,
  "maxBuildingsPerQuery": 100,
  "includeUnchanged": false,
  "maxItems": 1000
}
```

# Actor output Schema

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

Dataset with one row per building (ID-RNB, status, point, footprint, addresses, BD TOPO / BDNB ids, parcels), or one row per entry when no building is returned

## `runReport` (type: `string`):

Summary of the run (entries, ok / no data / failed counts, charged and free rows, requests to the RNB, stop reason)

# 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 = {
    "queries": [
        "9QX7W3BF6RP6",
        "11 Allee du Garde, 33360 Cénac",
        "33118000AH0084"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mouadapi/france-buildings").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 = { "queries": [
        "9QX7W3BF6RP6",
        "11 Allee du Garde, 33360 Cénac",
        "33118000AH0084",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("mouadapi/france-buildings").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 '{
  "queries": [
    "9QX7W3BF6RP6",
    "11 Allee du Garde, 33360 Cénac",
    "33118000AH0084"
  ]
}' |
apify call mouadapi/france-buildings --silent --output-dataset

```

## MCP server setup

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

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/UzewaP7lXbuUQ3t2R/builds/3CYfBrRsTmzJteuiT/openapi.json
