# France Address Geocoder API (Géocodage BAN): INSEE, GPS (`actose/france-address-geocoder`) Actor

Geocode French addresses in bulk with the official BAN (IGN Géoplateforme): normalized address, match score, INSEE code, postcode, GPS and Lambert 93. Reverse geocoding too. No API key, no login. MCP-ready for AI agents. Pay per result, not found is free. $0.30 per 1,000 addresses

- **URL**: https://apify.com/actose/france-address-geocoder.md
- **Developed by:** [ABM](https://apify.com/actose) (community)
- **Categories:** Developer tools, Real estate
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.26 / 1,000 address founds

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

## France Address Geocoder API: bulk geocoding with the official BAN (Géocodage d'adresses)

Turn French postal addresses into **GPS coordinates, normalized addresses, INSEE municipality codes and postcodes**, or turn coordinates back into the nearest address (reverse geocoding). The data comes from the **Base Adresse Nationale (BAN)**, the official French address register, served by the IGN **Géoplateforme** (`data.geopf.fr`). When that endpoint is down, the actor switches to `api-adresse.data.gouv.fr`, which serves the same BAN data.

- **Price: $0.30 per 1,000 addresses found** ($0.0003 per address). Not found, invalid and duplicate inputs are **free**.
- No API key, no login, no monthly rental.
- Bulk: thousands of addresses per run, about 1,000 addresses in under a minute.
- Every result has a **match score** (0 to 1) so you can tell exact matches from approximate ones.
- Ready for AI agents (MCP), the Apify API, Make, Zapier, n8n and Google Sheets.

### How much does it cost?

You pay one `address` event ($0.0003) for each input that returns an official address. Nothing else is billed.

| Volume | Cost |
|---|---|
| 100 addresses found | $0.03 |
| 1,000 addresses found | **$0.30** |
| 10,000 addresses found | $3.00 |
| 100,000 addresses found | $30.00 |

If 1,000 inputs give 950 matches and 50 inputs are not found, you pay $0.285. With "Candidates per address" set to 5, you still pay once per input. If you set a maximum cost per run, the actor stops cleanly when it is reached and never delivers unpaid results.

### What data do you get?

| Field | Example | Meaning |
|---|---|---|
| `query` | `1 place du Capitole 31000 Toulouse` | Your input |
| `queryType` | `forward` / `reverse` | Address → coordinates, or coordinates → address |
| `status` | `found` / `notFound` / `error` | Outcome (only `found` is charged) |
| `score` | `0.9765` | BAN match score, from 0 to 1 |
| `label` | `1 Place du Capitole 31000 Toulouse` | Normalized official address |
| `resultType` | `housenumber` | Precision: housenumber, street, locality, municipality |
| `houseNumber`, `street`, `name` | `1`, `Place du Capitole` | Address parts |
| `postalCode`, `city` | `31000`, `Toulouse` | |
| `inseeCode` | `31555` | INSEE municipality code (code commune) |
| `oldInseeCode`, `oldCity` | | Former municipality, after a merger |
| `districtName` | `Paris 2e Arrondissement` | Arrondissement (Paris, Lyon, Marseille) |
| `departmentCode`, `departmentName`, `region` | `31`, `Haute-Garonne`, `Occitanie` | |
| `latitude`, `longitude` | `43.603872`, `1.444053` | WGS84 (GPS) |
| `lambert93X`, `lambert93Y` | `574344.14`, `6279551.2` | Official French projection (EPSG:2154) |
| `distanceMeters` | `38` | Reverse geocoding: distance to your point |
| `banId`, `banAddressKey` | `264c0325-…`, `31555_1676_00001` | BAN identifiers, for joins with other datasets |
| `source` | `geoplateforme` | Endpoint that answered |
| `error` | `no address with score >= 0.7` | Reason when not found |

### Input example

```json
{
  "addresses": [
    "1 place du Capitole 31000 Toulouse",
    "12 rue de la Paix 75002 Paris",
    "15 av des champs elysee paris"
  ],
  "coordinates": ["43.604652,1.444209"],
  "minScore": 0.7,
  "maxResultsPerAddress": 1,
  "resultType": "any",
  "includeNotFound": true
}
```

| Option | Default | Description |
|---|---|---|
| `addresses` | | One address per line, free text. A postcode or city improves accuracy. |
| `coordinates` | | One `latitude,longitude` pair per line (WGS84). |
| `minScore` | `0.7` | Matches below this score are returned as `notFound` and not charged. |
| `maxResultsPerAddress` | `1` | Up to 10 candidates per input, still charged once. |
| `resultType` | `any` | Keep only `housenumber`, `street`, `locality` or `municipality` results. |
| `postcode`, `inseeCode` | | Restrict all forward queries to one postcode or one municipality. |
| `includeNotFound` | `true` | Adds a free row for each input with no result, so you can match every line back. |
| `service` | `geoplateforme` | Official endpoint to try first. The other one is the automatic fallback. |
| `maxConcurrency` | `10` | Parallel requests. The actor stays under 25 requests per second. |

### Output example

```json
[
  {
    "query": "1 place du Capitole 31000 Toulouse",
    "queryType": "forward",
    "status": "found",
    "rank": 1,
    "score": 0.9765,
    "label": "1 Place du Capitole 31000 Toulouse",
    "resultType": "housenumber",
    "houseNumber": "1",
    "street": "Place du Capitole",
    "name": "1 Place du Capitole",
    "postalCode": "31000",
    "city": "Toulouse",
    "inseeCode": "31555",
    "oldInseeCode": null,
    "oldCity": null,
    "districtName": null,
    "departmentCode": "31",
    "departmentName": "Haute-Garonne",
    "region": "Occitanie",
    "latitude": 43.603872,
    "longitude": 1.444053,
    "lambert93X": 574344.14,
    "lambert93Y": 6279551.2,
    "distanceMeters": null,
    "banId": "264c0325-241b-4c4e-a07f-896b6b2c1b03",
    "banAddressKey": "31555_1676_00001",
    "importance": 0.74128,
    "inputLatitude": null,
    "inputLongitude": null,
    "source": "geoplateforme",
    "error": null,
    "geocodedAt": "2026-10-19T08:00:00+00:00"
  },
  {
    "query": "43.604652,1.444209",
    "queryType": "reverse",
    "status": "found",
    "score": 0.9962,
    "label": "5 Rue Lafayette 31000 Toulouse",
    "postalCode": "31000",
    "city": "Toulouse",
    "inseeCode": "31555",
    "latitude": 43.604993,
    "longitude": 1.444281,
    "distanceMeters": 38,
    "banAddressKey": "31555_4920_00005"
  },
  {
    "query": "42 rue des Licornes Volantes 31000 Toulouse",
    "queryType": "forward",
    "status": "notFound",
    "label": null,
    "error": "no address with score >= 0.7"
  }
]
```

Results are in the run's dataset: export them as JSON, CSV, Excel or XML, or open the "Overview" table.

### Use cases

#### 1. CRM cleanup for a sales operations team

A sales ops manager exports 8,000 B2B customer addresses from the CRM. Many have abbreviations ("bd", "av"), missing postcodes or typos. One run fixes them for **$2.40** (8,000 × $0.0003).

Input:

```json
{ "addresses": ["15 av des champs elysee paris", "2 allees jules guesde toulouse"], "minScore": 0.7 }
```

Output excerpt:

```json
[{ "query": "15 av des champs elysee paris", "score": 0.786, "label": "15 Avenue des Champs Élysées 75008 Paris", "postalCode": "75008", "inseeCode": "75056" }]
```

Next step: the `label`, `postalCode` and `inseeCode` columns go back into the CRM. Rows below 0.8 are sent to a person for review.

#### 2. Real estate and geomarketing: joining listings with INSEE statistics

A real estate analyst gets 2,500 property addresses in Haute-Garonne every week and needs the INSEE code to join with INSEE income data and DVF sale prices. The analyst schedules the actor every Monday and pays **$0.75 per week**.

Input:

```json
{ "addresses": ["118 route de Narbonne Toulouse", "20 boulevard Lazare Carnot Toulouse"], "inseeCode": "31555", "resultType": "housenumber" }
```

Output excerpt:

```json
[{ "label": "118 Route de Narbonne 31400 Toulouse", "inseeCode": "31555", "latitude": 43.561566, "longitude": 1.463029, "lambert93X": 575784.9, "lambert93Y": 6274820.12 }]
```

Next step: `inseeCode` joins with INSEE tables. The Lambert 93 coordinates load directly into QGIS with French cadastral layers.

#### 3. Field service and delivery: GPS points back to addresses

A logistics coordinator receives 1,200 GPS points per day from technicians' phones and needs the street address for each intervention report. Cost: **$0.36 per day**.

Input:

```json
{ "coordinates": ["43.604652,1.444209", "48.858370,2.294481"] }
```

Output excerpt:

```json
[{ "query": "43.604652,1.444209", "label": "5 Rue Lafayette 31000 Toulouse", "distanceMeters": 38 }]
```

Next step: the address and the distance (a quality check on the GPS fix) go into the daily report in Google Sheets.

#### 4. Public tenders and local government data

A consultant working for a département geocodes the 3,000 addresses of public facilities (schools, town halls) to map them by municipality. The cost is $0.90. The `banId` gives a stable identifier for future updates.

### Call it from the API

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<username>/france-address-geocoder").call(run_input={
    "addresses": ["1 place du Capitole 31000 Toulouse", "12 rue de la Paix 75002 Paris"],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["query"], "→", item["label"], item["inseeCode"], item["latitude"], item["longitude"])
```

**JavaScript**

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('<username>/france-address-geocoder').call({
    addresses: ['1 place du Capitole 31000 Toulouse', '12 rue de la Paix 75002 Paris'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => [i.query, i.label, i.inseeCode]));
```

**One HTTP call (synchronous, returns the results directly)**

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~france-address-geocoder/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"addresses": ["1 place du Capitole 31000 Toulouse"]}'
```

### Integrations: Make, Zapier, n8n, Google Sheets

**n8n example (geocode new rows of a Google Sheet every morning):**

1. *Schedule Trigger*: every day at 7:00.
2. *Google Sheets → Get rows* where the `lat` column is empty.
3. *Apify → Run actor and get dataset*: actor `france-address-geocoder`, input `{"addresses": {{ $json.address }}}` (aggregate the rows into one list first, for one run per day).
4. *Google Sheets → Update row*, matched on `query`: write `label`, `postalCode`, `inseeCode`, `latitude`, `longitude`, `score`.

You can build the same flow in Make with the Apify module "Run an Actor", or in Zapier with the "Run Actor" action. Apify also provides a native Google Sheets export and webhooks.

### Use with AI agents (MCP)

The actor is available through the Apify MCP server, so Claude, Cursor, ChatGPT and other MCP clients can call it as a tool. Add `https://mcp.apify.com/?actors=<username>/france-address-geocoder` to your MCP client config. The agent can then ask, for example, "find the INSEE code and GPS coordinates of these 50 addresses". The flat JSON output (one row per input with an explicit `status`) is easy for an LLM to read, and the `label` field is a clean one-line address for prompts and RAG documents.

### FAQ

**Which official service is used?**
The Géoplateforme geocoding API of IGN (`data.geopf.fr/geocodage`), which serves the Base Adresse Nationale. When it does not answer, the actor uses `api-adresse.data.gouv.fr` (same BAN data). The `source` field shows which one answered.

**How do I read the score?**
A score above 0.9 is an exact match. Between 0.7 and 0.9 the address was corrected (typo, abbreviation, missing postcode). Below 0.7 the match is usually a different address, so it is returned as `notFound` and not charged. Change `minScore` to be stricter or more tolerant.

**Do I pay for duplicates or invalid lines?**
No. Duplicates are removed before any request. Lines with no result, invalid coordinates or a server error are free.

**Does it work outside France?**
It covers metropolitan France and the overseas départements (Guadeloupe, Martinique, Guyane, La Réunion, Mayotte), because those are the areas in the BAN. Addresses in other countries return `notFound`.

**Can I geocode POIs or cadastral parcels?**
No. This actor uses the address index only.

**How fast is it?**
About 25 addresses per second, so roughly 1,000 addresses in under a minute and 100,000 in a bit over an hour.

### Limits

- France only (BAN coverage). New addresses appear once a municipality publishes them to the BAN.
- Some rural addresses are only located at the street or locality level (`resultType` = `street` or `locality`). Use `resultType: "housenumber"` if you only want exact points.
- The score measures how close the text is to the official address, not whether the address exists. Check results between 0.7 and 0.8 before relying on them.
- The official API allows 50 requests per second per IP. The actor stays at 25 to remain reliable. Very large batches (over 1 million rows) take several hours.
- Results depend on the availability of the public service. When both official endpoints are down, the run stops with a clear message and nothing is charged.

# Actor input Schema

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

One French postal address per line, free text (e.g. "1 place du Capitole 31000 Toulouse"). Adding the postcode or city improves accuracy. Duplicates are processed and charged once.

## `coordinates` (type: `array`):

One "latitude,longitude" pair per line in WGS84 decimal degrees (e.g. "43.6045,1.4442"). Returns the nearest official address with its distance in meters.

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

Forward geocoding only. Matches below this BAN score (0 to 1) are returned as "notFound" and not charged. 0.7 rejects invented addresses while keeping common typos and abbreviations ("av", "bd").

## `maxResultsPerAddress` (type: `integer`):

Number of candidates returned per input (1 to 10). The best one has rank 1. Only one event is charged per input, whatever this number.

## `resultType` (type: `string`):

Restrict results to one level of precision. Leave empty to get the best match at any level.

## `postcode` (type: `string`):

Optional. Only return addresses with this postcode (e.g. "31000"). Applies to every forward query of the run.

## `inseeCode` (type: `string`):

Optional. Only return addresses in this municipality, by its INSEE code (e.g. "31555" for Toulouse, "75056" for Paris). Applies to every forward query of the run.

## `includeNotFound` (type: `boolean`):

Adds a free row with status "notFound" or "error" for each input that has no result, so every input line can be matched back in your spreadsheet. These rows are never charged.

## `service` (type: `string`):

Which official BAN endpoint to query first. The other one is used automatically if the first is down.

## `maxConcurrency` (type: `integer`):

Requests in flight at the same time. The run never exceeds 25 requests per second, below the official limit of 50 per second.

## Actor input object example

```json
{
  "addresses": [
    "1 place du Capitole 31000 Toulouse",
    "12 rue de la Paix 75002 Paris",
    "2 allées Jules Guesde Toulouse"
  ],
  "coordinates": [
    "43.604652,1.444209"
  ],
  "minScore": 0.7,
  "maxResultsPerAddress": 1,
  "resultType": "any",
  "includeNotFound": true,
  "service": "geoplateforme",
  "maxConcurrency": 10
}
```

# Actor output Schema

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

Dataset with the normalized address, score, INSEE code, postcode and coordinates of every input.

## `resultsCsv` (type: `string`):

All fields as a CSV file, ready for Excel or Google Sheets.

# 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": [
        "1 place du Capitole 31000 Toulouse",
        "12 rue de la Paix 75002 Paris",
        "2 allées Jules Guesde Toulouse"
    ],
    "coordinates": [
        "43.604652,1.444209"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("actose/france-address-geocoder").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": [
        "1 place du Capitole 31000 Toulouse",
        "12 rue de la Paix 75002 Paris",
        "2 allées Jules Guesde Toulouse",
    ],
    "coordinates": ["43.604652,1.444209"],
}

# Run the Actor and wait for it to finish
run = client.actor("actose/france-address-geocoder").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": [
    "1 place du Capitole 31000 Toulouse",
    "12 rue de la Paix 75002 Paris",
    "2 allées Jules Guesde Toulouse"
  ],
  "coordinates": [
    "43.604652,1.444209"
  ]
}' |
apify call actose/france-address-geocoder --silent --output-dataset

```

## MCP server setup

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

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/loHT0tdtkLlIPi1Rf/builds/UrernnHM9xHHUtY2T/openapi.json
