# French Company Register Search: SIREN, SIRET, NAF, VAT Number (`yadroo/france-company-search`) Actor

Company records from France's official government register: legal name, SIREN, SIRET, NAF code, legal form, VAT number, headquarters and branch addresses, creation date, headcount band, revenue and status. Search by name, identifier, postcode, department, activity or map radius. UTC dates.

- **URL**: https://apify.com/yadroo/france-company-search.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** Business, Lead generation, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 company records

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

Company records from France's official government register: legal name, SIREN, SIRET, NAF code, legal form, VAT number, headquarters and branch addresses, creation date, headcount band, revenue and status. Search by name, identifier, postcode, department, activity or map radius. UTC dates.

Every row is one legal unit (an *unite legale*) of the French register of businesses and their establishments, read from the government's own public search service — no API key, no proxy, no browser. Search by words and filters, resolve a column of SIREN or SIRET numbers into verified records, or list the companies with a site inside a radius around a map point. Built for sales and market research, procurement and KYB onboarding, credit and compliance checks, and for agents that need flat, declarative company data. Directors, shareholders, e-mail addresses and phone numbers are never read and never written: this actor returns company data only.

> **Need it tailored to your workflow?** Missing a field, a filter, another source or a different output format? [Open an issue](https://apify.com/yadroo/france-company-search/issues) and describe what you need — we read every request and adapt the actor.

### Use cases

- **Local market research and territory planning**: every company trading in a postcode, a commune or a department, with the address of the site that actually sits there — a chain's branch is reported at its own address, not at the head office.
- **Bulk SIREN / SIRET verification for onboarding and CRM clean-up**: paste a column of identifiers and get one row each with legal name, legal form, VAT number, activity code and status; a number the register does not know comes back marked `found: false`.
- **Sector mapping and B2B targeting**: companies by NAF activity code, activity section, department and headcount band — the list a recruiter, analyst or sales team needs before a campaign.
- **Catchment and field-sales analysis**: all companies with a registered site inside a radius around coordinates, with latitude and longitude, ready for a map.
- **Certified supplier lists**: organisations the register cross-links to an official label — social economy (ESS), Qualiopi training quality, RGE energy-renovation work, certified organic, work-integration enterprises, mission-driven companies and nine more.
- **Risk, audit and supplier-file hygiene**: struck-off companies by trade and area with the date they ceased, so a supplier file can be cleaned of entities that no longer exist.
- **Deal sourcing by size class**: intermediate-sized (ETI) or large (GE) companies in a region, biggest first, with published revenue and net income where the company files them.

### Input

No field is required. `mode` decides which of the three questions a run asks; filters that do not belong to that mode are ignored.

| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
| `mode` | string | `search` | `search`, `identifiers`, `nearPoint`. Dropdown: the platform refuses any other value before the run starts. |
| `query` | string | empty | Free text matched by the register against company names, trade names and addresses. Passed on exactly as you type it; case and accents do not matter. May be empty when at least one filter is set. |
| `identifiers` | string\[] | empty | `identifiers` mode. SIREN (9 digits) or SIRET (14 digits); spaces, dots and dashes are removed. One row per entry, in the order given. Anything that is not 9 or 14 digits is refused with the value named. |
| `latitude`, `longitude` | number | empty | `nearPoint` mode. Decimal degrees, WGS84 (−90..90 and −180..180). |
| `radiusKm` | number | 5 | 0.1–50 km; the source refuses more than 50. |
| `postalCodes` | string\[] | empty | Five-digit postcodes, e.g. `75011`, `69002`. Matches establishments. Several values = OR. |
| `departments` | string\[] | empty | Department codes — see [Departments](#departments). A leading zero may be left out (`1` = `01`), `2A`/`2B` for Corsica, three digits overseas. An unknown code is refused with the closest valid ones named. |
| `communeCodes` | string\[] | empty | Five-character INSEE commune codes, e.g. `69123` (Lyon), `2A004` (Ajaccio). Not postcodes. |
| `regionCodes` | string\[] | empty | Two-digit region codes — see [Regions](#regions). Leading zero optional. Unknown codes are refused with a suggestion. |
| `activityCodes` | string\[] | empty | NAF/APE 2008 codes, e.g. `56.10A`, `62.01Z`, `47.11D`. The dot may be left out (`5610A`) and the letter may be lower case. The 732 valid codes are the ones the register itself accepts; anything else is refused with the closest codes named, never widened to the division. |
| `activitySections` | string\[] | empty | One letter per branch of the economy — see [Activity sections](#activity-sections). Dropdown: an off-list letter is refused by the platform. |
| `legalFormCodes` | string\[] | empty | Four-digit legal category codes, e.g. `5710`, `5499`, `9220` — see [Legal forms](#legal-forms). Any of the 260 codes the register accepts may be sent; an invalid one is refused with a suggestion. |
| `companySize` | string | empty | `PME`, `ETI`, `GE` (small/medium, intermediate, large). Dropdown, single choice: the register does not accept a list here. |
| `headcountBands` | string\[] | empty | Band codes `NN`, `00`, `01`, `02`, `03`, `11`, `12`, `21`, `22`, `31`, `32`, `41`, `42`, `51`, `52`, `53` — see [Headcount bands](#headcount-bands). Dropdown with plain-word titles; several bands = OR. |
| `status` | string | `active` | `active`, `ceased`, `any`. Active by default, because a plain register search otherwise returns decades of struck-off entities next to the live ones. Dropdown. |
| `labels` | string\[] | empty | Official labels and registers — see [Labels](#labels). Several labels narrow each other (AND). Dropdown. |
| `revenueMinEur`, `revenueMaxEur` | number | empty | Bounds on the latest **publicly filed** annual revenue, in euros. Companies that filed nothing are dropped by these filters. |
| `netIncomeMinEur`, `netIncomeMaxEur` | number | empty | Bounds on the latest publicly filed net result; negative values allowed. |
| `sortBySize` | boolean | `false` | Order by number of establishments instead of the register's relevance ranking. |
| `includeEstablishments` | boolean | `false` | Add an `establishments` array with every matching site of the company. |
| `maxEstablishmentsPerCompany` | number | 3 | 1–100. Caps the sites the source sends back per company and the `establishments` array. Does not change the row count or the cost. |
| `includeFinancials` | boolean | `true` | Fill `revenueEur`, `netIncomeEur` and `financialsYear`; off leaves those three columns out. |
| `onlyNew` | boolean | `false` | Remember the SIREN numbers written and, on the next run of the same actor or task, write only companies not written before. |
| `maxItems` | number | 50 | 1–1000. Hard cap on the companies written, and so on the cost. |
| `fields` | string\[] | all | Keep only these columns, **in this order** — e.g. `["name","siren","matchedCity"]`. `found`, `siren`, `name`, `status`, `url` and `fetchedAt` are always kept (first, in that order, unless you list them — a listed one moves to the position you gave it). A name that is not a column is reported in the run's status message instead of being dropped in silence. |

**The one rule that matters about places.** `postalCodes`, `communeCodes`, `departments`, `regionCodes` and a radius search match **establishments**, not head offices. A bakery chain registered in the 2nd arrondissement is returned for `postalCodes: ["75011"]` because it runs a shop there. The site that matched is in the `matched…` columns (`matchedAddress`, `matchedPostalCode`, `matchedCity`, `matchedDepartment`, `matchedRegion`, `latitude`, `longitude`); the head office is in the `hq…` columns. When a run carries no place filter, the `matched…` columns describe the head office.

**Typos.** `departments`, `regionCodes`, `activityCodes` and `legalFormCodes` are free text: case, spaces, accents, a missing leading zero and a missing dot in a NAF code are all accepted, and any other spelling is refused with a message naming the closest valid codes — the run never silently searches something else. `mode`, `status`, `companySize`, `activitySections`, `headcountBands` and `labels` are dropdowns: the platform itself refuses a value outside the list before the run starts, with the allowed values in the error, so there is nothing to correct. `query` is passed to the register as typed; `postalCodes`, `communeCodes` and `identifiers` are checked for their format.

### Reference

#### Activity sections

`A` Agriculture, forestry and fishing · `B` Mining and quarrying · `C` Manufacturing · `D` Electricity, gas and steam supply · `E` Water, sewerage and waste · `F` Construction · `G` Wholesale, retail and vehicle repair · `H` Transport and storage · `I` Hotels, restaurants and catering · `J` Information and communication · `K` Finance and insurance · `L` Real estate · `M` Professional, scientific and technical work · `N` Administrative and support services · `O` Public administration and defence · `P` Education · `Q` Health and social work · `R` Arts, entertainment and recreation · `S` Other services · `T` Households as employers · `U` Extraterritorial bodies

A section is the level above the NAF code: picking section `J` and code `62.01Z` narrows to that code inside that section. Rows carry `activitySection` and `activitySectionLabel`.

#### Headcount bands

| Code | Staff | Code | Staff |
|---|---|---|---|
| `NN` | Not declared | `22` | 100 to 199 employees |
| `00` | 0 employees | `31` | 200 to 249 employees |
| `01` | 1 to 2 employees | `32` | 250 to 499 employees |
| `02` | 3 to 5 employees | `41` | 500 to 999 employees |
| `03` | 6 to 9 employees | `42` | 1000 to 1999 employees |
| `11` | 10 to 19 employees | `51` | 2000 to 4999 employees |
| `12` | 20 to 49 employees | `52` | 5000 to 9999 employees |
| `21` | 50 to 99 employees | `53` | 10000 employees or more |

The register publishes bands, never exact staff numbers. `NN` is a large group — sole traders, holdings and companies that have filed nothing yet — so leave it out when you want real employers. Rows carry both `headcountBandCode` and `headcountBand`, plus `headcountYear` (the band is revised yearly).

#### Labels

| Key | What the register says |
|---|---|
| `association` | Declared association |
| `ess` | Part of the social and solidarity economy (ESS) |
| `societeMission` | Mission-driven company |
| `entrepreneurIndividuel` | Sole trader |
| `rge` | RGE — certified for energy renovation work |
| `qualiopi` | Qualiopi — certified training quality |
| `organismeFormation` | Registered training provider |
| `bio` | Certified organic operator |
| `siae` | Work-integration social enterprise |
| `achatsResponsables` | Responsible purchasing label |
| `entrepreneurSpectacle` | Live-performance employer licence |
| `patrimoineVivant` | Living heritage company label |
| `finess` | Health or social care facility (FINESS) |
| `uai` | Education establishment (UAI) |
| `collectiviteTerritoriale` | Local authority |
| `administration` | Public administration body |

The labels a company carries are written to the `labels` column, whether you filtered by them or not.

#### Legal forms

Any of the register's 260 four-digit legal category codes can be used in `legalFormCodes`. These are the ones this actor also puts into words in `legalFormLabel`; every other code comes back with `legalFormCode` filled and `legalFormLabel` empty.

| Code | Legal form | Code | Legal form |
|---|---|---|---|
| `1000` | Sole trader | `5710` | Simplified joint-stock company (SAS, SASU) |
| `5202` | General partnership (SNC) | `5800` | European company (SE) |
| `5306` | Limited partnership (SCS) | `6540` | Non-trading property company (SCI) |
| `5370` | Partnership limited by shares (SCA) | `7210` | Municipality |
| `5431` | Worker cooperative with limited liability (SCOP) | `7220` | Department authority |
| `5499` | Private limited company (SARL, EURL) | `7230` | Regional authority |
| `5599` | Public limited company with a board of directors (SA) | `9220` | Declared association |
| `5699` | Public limited company with a management board (SA) | `9230` | Association serving the public interest |
| | | `9300` | Foundation |

#### Departments

`01` Ain · `02` Aisne · `03` Allier · `04` Alpes-de-Haute-Provence · `05` Hautes-Alpes · `06` Alpes-Maritimes · `07` Ardeche · `08` Ardennes · `09` Ariege · `10` Aube · `11` Aude · `12` Aveyron · `13` Bouches-du-Rhone · `14` Calvados · `15` Cantal · `16` Charente · `17` Charente-Maritime · `18` Cher · `19` Correze · `21` Cote-d'Or · `22` Cotes-d'Armor · `23` Creuse · `24` Dordogne · `25` Doubs · `26` Drome · `27` Eure · `28` Eure-et-Loir · `29` Finistere · `2A` Corse-du-Sud · `2B` Haute-Corse · `30` Gard · `31` Haute-Garonne · `32` Gers · `33` Gironde · `34` Herault · `35` Ille-et-Vilaine · `36` Indre · `37` Indre-et-Loire · `38` Isere · `39` Jura · `40` Landes · `41` Loir-et-Cher · `42` Loire · `43` Haute-Loire · `44` Loire-Atlantique · `45` Loiret · `46` Lot · `47` Lot-et-Garonne · `48` Lozere · `49` Maine-et-Loire · `50` Manche · `51` Marne · `52` Haute-Marne · `53` Mayenne · `54` Meurthe-et-Moselle · `55` Meuse · `56` Morbihan · `57` Moselle · `58` Nievre · `59` Nord · `60` Oise · `61` Orne · `62` Pas-de-Calais · `63` Puy-de-Dome · `64` Pyrenees-Atlantiques · `65` Hautes-Pyrenees · `66` Pyrenees-Orientales · `67` Bas-Rhin · `68` Haut-Rhin · `69` Rhone · `70` Haute-Saone · `71` Saone-et-Loire · `72` Sarthe · `73` Savoie · `74` Haute-Savoie · `75` Paris · `76` Seine-Maritime · `77` Seine-et-Marne · `78` Yvelines · `79` Deux-Sevres · `80` Somme · `81` Tarn · `82` Tarn-et-Garonne · `83` Var · `84` Vaucluse · `85` Vendee · `86` Vienne · `87` Haute-Vienne · `88` Vosges · `89` Yonne · `90` Territoire de Belfort · `91` Essonne · `92` Hauts-de-Seine · `93` Seine-Saint-Denis · `94` Val-de-Marne · `95` Val-d'Oise · `971` Guadeloupe · `972` Martinique · `973` French Guiana · `974` Reunion · `975` Saint-Pierre-et-Miquelon · `976` Mayotte · `977` Saint-Barthelemy · `978` Saint-Martin · `984` French Southern and Antarctic Lands · `986` Wallis and Futuna · `987` French Polynesia · `988` New Caledonia · `989` Clipperton Island

There is no department `20`: Corsica is `2A` and `2B`.

#### Regions

`01` Guadeloupe · `02` Martinique · `03` French Guiana · `04` Reunion · `06` Mayotte · `11` Ile-de-France · `24` Centre-Val de Loire · `27` Bourgogne-Franche-Comte · `28` Normandie · `32` Hauts-de-France · `44` Grand Est · `52` Pays de la Loire · `53` Bretagne · `75` Nouvelle-Aquitaine · `76` Occitanie · `84` Auvergne-Rhone-Alpes · `93` Provence-Alpes-Cote d'Azur · `94` Corse

### Examples

**Bakeries trading in one Paris postcode** — local market research; the matched branch address is what you chart.

```json
{ "mode": "search", "query": "boulangerie", "postalCodes": ["75011"], "status": "active", "maxItems": 15 }
```

**Verify a column of SIREN and SIRET numbers** — onboarding and CRM clean-up, with only the columns an import needs.

```json
{ "mode": "identifiers", "identifiers": ["652014051", "65201405100732", "477550370"], "fields": ["name", "siren", "vatNumber", "legalFormLabel", "matchedAddress"], "maxItems": 10 }
```

**Software firms of 20 to 49 staff in the Rhone department** — sector mapping by NAF code and size.

```json
{ "mode": "search", "departments": ["69"], "activityCodes": ["62.01Z"], "headcountBands": ["12"], "status": "active", "maxItems": 15 }
```

**Restaurants within one kilometre of central Paris** — catchment analysis, rows ready for a map.

```json
{ "mode": "nearPoint", "latitude": 48.8566, "longitude": 2.3522, "radiusKm": 1, "activityCodes": ["56.10A"], "status": "active", "maxItems": 15 }
```

**Social-economy organisations in Gironde** — responsible procurement and grant programmes.

```json
{ "mode": "search", "departments": ["33"], "labels": ["ess"], "status": "active", "maxItems": 15 }
```

**Mid-sized companies in Auvergne-Rhone-Alpes, biggest first** — deal sourcing and account planning.

```json
{ "mode": "search", "regionCodes": ["84"], "companySize": "ETI", "status": "active", "sortBySize": true, "maxItems": 15 }
```

**Struck-off transport companies in Bouches-du-Rhone** — supplier-file hygiene and credit review.

```json
{ "mode": "search", "query": "transport", "departments": ["13"], "status": "ceased", "maxItems": 15 }
```

### Output

One row per company. A real row from a cloud run of the first example (long arrays trimmed with "…"):

```json
{
  "found": true,
  "notFoundReason": null,
  "inputIdentifier": null,
  "sourceMode": "search",
  "siren": "812809234",
  "name": "BOULANGERIE DU NIL",
  "legalName": "BOULANGERIE DU NIL",
  "sigle": null,
  "status": "active",
  "statusCode": "A",
  "isPubliclyListed": true,
  "legalFormCode": "5499",
  "legalFormLabel": "Private limited company (SARL or EURL)",
  "activityCode": "10.71C",
  "activityCode2025": "10.71H",
  "activitySection": "C",
  "activitySectionLabel": "Manufacturing",
  "companyCategory": "PME",
  "companyCategoryYear": 2024,
  "headcountBand": "20 to 49 employees",
  "headcountBandCode": "12",
  "headcountYear": 2024,
  "isEmployer": true,
  "creationDate": "2015-07-02",
  "closureDate": null,
  "lastUpdate": "2026-10-06T18:01:26.000Z",
  "establishmentCount": 6,
  "openEstablishmentCount": 5,
  "vatNumber": "FR04812809234",
  "hqSiret": "81280923400014",
  "hqAddress": "7 RUE DU NIL 75002 PARIS",
  "hqPostalCode": "75002",
  "hqCity": "PARIS",
  "hqDepartment": "75",
  "hqRegion": "11",
  "hqLatitude": 48.867702377,
  "hqLongitude": 2.3478165515,
  "hqIsOpen": true,
  "matchedSiret": "81280923400030",
  "matchedAddress": "8 RUE PAUL BERT 75011 PARIS",
  "matchedPostalCode": "75011",
  "matchedCity": "PARIS",
  "matchedDepartment": "75",
  "matchedRegion": "11",
  "latitude": 48.851471801,
  "longitude": 2.3846570761,
  "isHeadquarters": false,
  "matchedEstablishmentCount": 2,
  "revenueEur": 0,
  "netIncomeEur": 437133,
  "financialsYear": 2023,
  "labels": [],
  "collectiveAgreementIds": ["0843"],
  "url": "https://annuaire-entreprises.data.gouv.fr/entreprise/812809234",
  "fetchedAt": "2026-10-06T20:23:14.581Z"
}
```

| Field | Type | Meaning |
|---|---|---|
| `found` | boolean | Always present. `false` only in `identifiers` mode, for a number the register does not know. |
| `notFoundReason` | string | Why an identifier produced no record. |
| `inputIdentifier` | string | The identifier exactly as you typed it (`identifiers` mode). |
| `sourceMode` | string | `search`, `identifiers` or `nearPoint`. |
| `siren` | string | Always present. The company's 9-digit number. |
| `name` | string | Always present. The register's full name of the company. |
| `legalName` | string | Registered corporate name. |
| `sigle` | string | Short form or acronym, when the register holds one. |
| `status` | string | Always present. `active` or `ceased`. |
| `statusCode` | string | The register's own code: `A` or `C`. |
| `isPubliclyListed` | boolean | Whether the register publishes this record in full. `false` means the company asked for part of it to be withheld — nothing to do with a stock exchange. |
| `legalFormCode` / `legalFormLabel` | string | Legal category code, and our wording for the forms in the table above. |
| `activityCode` | string | NAF/APE 2008 code of the company's main activity. |
| `activityCode2025` | string | The same activity in the 2025 revision of the nomenclature. |
| `activitySection` / `activitySectionLabel` | string | Branch letter A–U and its name. |
| `companyCategory` / `companyCategoryYear` | string / number | Official size class (PME, ETI, GE) and the year it was assigned. |
| `headcountBand` / `headcountBandCode` / `headcountYear` | string / string / number | Staff band in words, its code, and the year of the band. |
| `isEmployer` | boolean | Whether the register marks the company as an employer. The flag is declared per site, so this column carries the head office's; empty when the register declares none. |
| `creationDate` / `closureDate` | date | Registration date, and the date the company was struck off (UTC, `YYYY-MM-DD`). |
| `lastUpdate` | date | When the register last changed this record (ISO 8601 UTC). |
| `establishmentCount` / `openEstablishmentCount` | number | Sites the company has, and sites still open. |
| `vatNumber` | string | Intra-EU VAT number, when the register derives one. |
| `hqSiret`, `hqAddress`, `hqPostalCode`, `hqCity`, `hqDepartment`, `hqRegion`, `hqLatitude`, `hqLongitude`, `hqIsOpen` | mixed | The head office. |
| `matchedSiret`, `matchedAddress`, `matchedPostalCode`, `matchedCity`, `matchedDepartment`, `matchedRegion`, `latitude`, `longitude`, `isHeadquarters` | mixed | The site that answered your filter (the head office when the run had no place filter). `isHeadquarters` says which it is. |
| `matchedEstablishmentCount` | number | How many matching sites the register returned for this company (capped by `maxEstablishmentsPerCompany`). |
| `establishments` | array | Only with `includeEstablishments`: every matching site with SIRET, address, postcode, town, commune code, department, region, coordinates, head-office flag, open flag, activity code and trade name. |
| `revenueEur`, `netIncomeEur`, `financialsYear` | number | Latest publicly filed revenue and net result, and their year. Only with `includeFinancials`; often empty (see Limits). |
| `labels` | array | The official labels this company carries (keys from the table above). |
| `collectiveAgreementIds` | array | Collective-agreement identifiers (IDCC) the register links to the company. |
| `url` | string | Always present. The company's page in the government's public register directory. |
| `fetchedAt` | date | Always present. When the row was read, ISO 8601 UTC. |

Always kept, whatever `fields` says: `found`, `siren`, `name`, `status`, `url`, `fetchedAt`.

Dataset views: **Companies** (name, SIREN, status, NAF, branch, staff, town, postcode, registration date, found, mode, link), **Addresses and map points** (matched SIRET, address, postcode, town, department, region, coordinates, head-office flag), **Size, form and accounts** (legal form, size class, staff, sites, VAT number, revenue, net income, labels).

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~france-company-search/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"search","query":"boulangerie","postalCodes":["75011"],"maxItems":15}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/france-company-search').call({ mode: 'identifiers', identifiers: ['652014051'], maxItems: 10 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/france-company-search").call(run_input={"mode": "search", "departments": ["69"], "activityCodes": ["62.01Z"], "maxItems": 15})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: add `https://mcp.apify.com` to Claude / Cursor / any MCP client and call the `yadroo/france-company-search` tool with the same JSON input. The filters are declarative and the row is flat, so an agent can ask one question per run and read the answer without parsing prose.

### Pricing

Pay per event: **$0.001 to start a run plus $0.002 per company written** (one row = one company record). Every run is charged the start event, including a run that finds nothing. Every row written to the dataset is charged — a `found: false` marker row for an unknown identifier is a row and is billed like any other. Prices here are the FREE-tier prices; paid Apify plans get the platform's own discount.

| Run | Cost |
|---|---|
| 15 companies (one of the examples above) | $0.001 + 15 × $0.002 = **$0.031** |
| 50 companies (the default `maxItems`) | $0.001 + 50 × $0.002 = **$0.101** |
| 1000 companies (the maximum per run) | $0.001 + 1000 × $0.002 = **$2.001** |

`maxItems` is the hard cap on what a run can cost. A run also stops at the **Maximum cost per run** you set on the run, keeps every row you already paid for, and says so in its status message ("Stopped at your spending limit: N rows delivered").

### Limits & FAQ

- **Speed, measured 2026-10-06 in the Apify cloud.** The register answers 25 companies per request. 1000 companies — the largest input the schema allows — took 40 requests and 43 seconds end to end, about 1400 companies a minute; a 15-row example finishes in 5-7 seconds. Nothing the input schema allows comes close to the 900-second run timeout, and a run at that size drew no throttling from the source.
- **The source's rate limit.** The service documents a maximum of 7 requests per second per IP (and 30 per second per network) and answers HTTP 429 with a `Retry-After` header when you pass it. This actor stays at 2 requests per second from a single token bucket that retries also pass through — under a third of the published budget, because it is a free public service. A 429 or a temporary slowdown is waited out and the run continues; it is not an error.
- **The register's 10 000-result window.** A search can page through at most 10 000 results (`page × per_page ≤ 10 000`), and the match count it reports stops at 10 000 too. When a filter has more matches, the run saves what fits and the status message says the window is used up and to split the search — by department, by activity code or by postcode. The run still succeeds.
- **A run that stops early still keeps its rows.** Before the run timeout the actor stops starting new work, writes what it has and ends SUCCEEDED with "Stopped before the run timeout: N rows saved" — measured on 2026-10-06: the same 1000-company input given a 20-second timeout saved 300 companies and ended with that message instead of timing out. The same happens at your maximum cost per run. Rows are written page by page, never in one batch at the end, so nothing is lost.
- **Revenue, net income and VAT number are often empty.** French companies may keep their accounts confidential, and the register derives a VAT number only for part of them. Setting `revenueMinEur` or `netIncomeMinEur` therefore also drops every company that filed nothing.
- **Activity codes come without a full label.** The register publishes codes, not names, for the 732 NAF codes; this actor labels the 21 sections (`activitySectionLabel`), the 16 headcount bands and the common legal forms, and returns `activityCode` as the code.
- **No "new companies since yesterday" filter exists.** The service has no creation-date filter, so repeat-run monitoring is done with `onlyNew`: the SIREN numbers written are remembered per actor or task, and the next run writes only what it has not written before.
- **`nearPoint` is a catchment tool, not a census.** It finds companies whose establishments have published coordinates; a site the register could not place on a map cannot be found this way. The endpoint accepts the activity filters only, so `status`, `postalCodes`, `communeCodes`, `departments`, `regionCodes`, `legalFormCodes`, `companySize`, `headcountBands`, `labels` and the revenue and net-income ranges are applied by this actor to each page **before** rows are written — so they never cost you anything. No distance column is returned, because the source gives none; use `latitude` and `longitude` if you need one.
- **Some records are partly withheld.** Companies that asked the register not to publish their details are missing from the search entirely, and a published record can still be partly masked — `isPubliclyListed: false` marks those.
- **Directors, e-mail addresses and phone numbers are deliberately not collected.** The register publishes officers; this actor never requests, reads or writes them, and the person-name search parameters are not exposed. Rows are company data only.
- **Unknown identifiers and empty results.** In `identifiers` mode an unknown number yields a row with `found: false` and `notFoundReason`. A filter combination that matches nothing yields no rows and a status message saying so — never a silent, broken-looking success. An input that cannot match anything at all is different: a reversed range (`revenueMinEur` above `revenueMaxEur`, the same for net income) or a malformed postcode, commune code, department, activity code, legal form or identifier ends the run FAILED before any request, with a message naming the values and what to send instead.
- **Questions or problems:** open an issue on the Issues tab of this actor.

***

Made by **Yadroo**. Sibling actors: [uk-planning-applications](https://apify.com/yadroo/uk-planning-applications), [world-bank-procurement](https://apify.com/yadroo/world-bank-procurement), [sanctions-screen](https://apify.com/yadroo/sanctions-screen), [domain-intel](https://apify.com/yadroo/domain-intel), [openfda-records](https://apify.com/yadroo/openfda-records).

# Actor input Schema

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

Three questions, one per run. `search` is the general register search: a word or phrase plus any of the filters below. `identifiers` turns a list of SIREN (9 digits) or SIRET (14 digits) numbers into one row each, which is what counterparty and CRM clean-up work needs - an identifier that is not in the register still gets a row with `found: false`. `nearPoint` asks the register which companies have a site within a radius of a latitude/longitude pair, for catchment and field-sales work.

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

Used by `search`. Free text matched against company names, trade names and addresses - a brand (`Carrefour`), a trade (`boulangerie`), a street and town, or a single SIREN/SIRET if you only have one. Case and accents do not matter. May be left empty when at least one filter below is set: `departments: ['69']` with `activityCodes: ['62.01Z']` is a valid run on its own.

## `identifiers` (type: `array`):

Used by `identifiers`. A SIREN is a company's 9-digit number, a SIRET the 14-digit number of one of its sites (SIREN + 5 digits). Spaces and dots are removed (`652 014 051`). Each entry produces exactly one row: `found: true` with the register record, or `found: false` with `notFoundReason` for a number the register does not know or does not publish. A SIRET is resolved to its company, with the site it names reported in the `matched…` columns.

## `latitude` (type: `number`):

Used by `nearPoint`. Decimal degrees, WGS84 - the format map tools copy out. The example is central Paris.

## `longitude` (type: `number`):

Used by `nearPoint`. Decimal degrees, WGS84.

## `radiusKm` (type: `number`):

How far from the point to look, in kilometres. The source refuses anything above 50 km. A company is returned when any of its establishments has published coordinates inside the circle; establishments whose address the register could not place on the map cannot be found this way, which is why a radius search is a catchment tool rather than a census.

## `postalCodes` (type: `array`):

Five-digit French postcodes, for example `75011`, `69002`, `33000`. A company is returned when any of its establishments sits in one of them - so a chain with its head office elsewhere is found by its local branch, and the branch that matched is written to `matchedPostalCode`, `matchedCity` and `matchedAddress`. Use those columns, not the `hq…` ones, when you filter by place.

## `departments` (type: `array`):

Department codes as the register writes them: two digits (`01` to `95`), `2A` and `2B` for Corsica, three digits for the overseas departments (`971` Guadeloupe, `972` Martinique, `973` French Guiana, `974` Reunion, `976` Mayotte). A leading zero may be left out (`1` is read as `01`). Unknown codes are refused with the closest valid ones named.

## `communeCodes` (type: `array`):

Five-character INSEE commune codes, for example `69123` for Lyon or `13055` for Marseille. These are not postcodes: one commune can cover several postcodes and one postcode several communes. Use this when you need exactly one town.

## `regionCodes` (type: `array`):

Two-digit region codes, for example `11` Ile-de-France, `84` Auvergne-Rhone-Alpes, `93` Provence-Alpes-Cote d'Azur, `75` Nouvelle-Aquitaine. The full list is in the README reference table. The region that matched is written to `matchedRegion`.

## `activityCodes` (type: `array`):

NAF (also called APE) activity codes in the 2008 revision, four digits and a letter: `56.10A` traditional restaurants, `62.01Z` computer programming, `47.11D` supermarkets, `41.20A` house building. The dot may be left out (`5610A`) and the letter may be lower case. A code that does not exist is refused with the closest valid codes named, never silently widened to the whole division. Several codes are combined with OR.

## `activitySections` (type: `array`):

The broad branch of the economy, one letter per section - the level above the NAF code, useful when you do not want to list twenty codes. Picking a section and a code at the same time narrows to codes inside that section. The platform refuses any letter outside A-U before the run starts.

## `legalFormCodes` (type: `array`):

Four-digit legal category codes of the register, for example `5710` simplified joint-stock company (SAS, SASU), `5499` private limited company (SARL, EURL), `9220` declared association, `1000` sole trader. The README reference table lists the codes this actor can also label in plain words; any valid four-digit code may be sent, and codes we cannot label still come back with `legalFormCode` filled and `legalFormLabel` empty.

## `companySize` (type: `string`):

The official size class the statistics office assigns from headcount, turnover and balance sheet. Leave empty for every size. This is a yearly classification, so it lags a fast-growing company by up to two years; `headcountBands` is the fresher signal.

## `headcountBands` (type: `array`):

Staff size as bands, because the register publishes bands and not exact numbers. Several bands are combined with OR, so `10 to 19` plus `20 to 49` gives companies of 10 to 49. `Not declared` is a large group: sole traders, holdings and companies that have filed nothing yet all sit there, so leave it out when you want real employers. Rows carry both the code (`headcountBandCode`) and the words (`headcountBand`).

## `status` (type: `string`):

Defaults to active companies, because a plain register search otherwise returns decades of dissolved entities next to the live ones. Set `ceased` for compliance and risk work - those rows carry `closureDate` - or `any` to see both in one dataset, told apart by the `status` column.

## `labels` (type: `array`):

Public certifications and registers the company appears in. Several labels narrow each other: picking `ess` and `qualiopi` returns social-economy organisations that are also certified trainers. These flags come from the register's own cross-links, so they are a reliable way to build a list that a directory site cannot give you. The labels a row carries are written to the `labels` column.

## `revenueMinEur` (type: `number`):

Keep companies whose latest published annual revenue is at least this much. Only companies that file their accounts publicly have a figure, and many French companies are allowed to keep theirs confidential - setting this filter therefore also drops everyone who filed nothing. `revenueEur` and `financialsYear` show what the figure is and which year it belongs to.

## `revenueMaxEur` (type: `number`):

Upper bound on the latest published annual revenue, in euros.

## `netIncomeMinEur` (type: `number`):

Lower bound on the latest published net result, in euros. Negative values are allowed, so `-1000000` to `0` finds loss-making filers.

## `netIncomeMaxEur` (type: `number`):

Upper bound on the latest published net result, in euros.

## `sortBySize` (type: `boolean`):

Order the answer by number of establishments instead of the source's relevance ranking. Useful when a broad filter would otherwise fill the row cap with dormant one-person entities; it does not change which companies match, only which ones you see first.

## `includeEstablishments` (type: `boolean`):

Add an `establishments` array with every site that matched your place or activity filter: SIRET, address, postcode, town, coordinates, whether it is the head office and whether it is open. Off by default, because one row per company is what most people chart; the `matched…` columns already carry the first matching site.

## `maxEstablishmentsPerCompany` (type: `number`):

Caps the `establishments` array and the number of sites the source sends back per company. A nationwide retailer can have thousands of them, so the default keeps responses small. Does not change the row count or the price - rows are counted per company.

## `includeFinancials` (type: `boolean`):

Fill `revenueEur`, `netIncomeEur` and `financialsYear` from the accounts the company filed publicly. Switch off for a narrower dataset; the filters above keep working either way.

## `onlyNew` (type: `boolean`):

Remember the SIREN numbers this run writes in the actor's key-value store and, on the next run of the same actor or task, write only companies that were not written before. Made for watching a filter over time - a department plus an activity code - so each run holds just what appeared since. The memory is kept per actor or task, so two tasks watching two regions do not interfere.

## `maxItems` (type: `number`):

Hard cap on the companies this run writes, and therefore on what it costs. Paging stops as soon as the cap is reached. The source itself hands out at most 10000 results per search, so a filter with more matches than that needs to be split - by department or by activity code - and the run says so in its status message.

## `fields` (type: `array`):

Keep only the named columns, in the order given - for example `name, siren, matchedCity, activityCode`. Empty = every column. `found`, `siren`, `name`, `status`, `url` and `fetchedAt` are always present; name one of them here and it moves to the position you gave it. Unknown names are reported in the status message instead of being dropped in silence.

## Actor input object example

```json
{
  "mode": "search",
  "query": "boulangerie",
  "identifiers": [
    "652014051",
    "65201405100732",
    "477550370"
  ],
  "latitude": 48.8566,
  "longitude": 2.3522,
  "radiusKm": 5,
  "postalCodes": [
    "75011"
  ],
  "status": "active",
  "sortBySize": false,
  "includeEstablishments": false,
  "maxEstablishmentsPerCompany": 3,
  "includeFinancials": true,
  "onlyNew": false,
  "maxItems": 50
}
```

# Actor output Schema

## `results` (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 = {
    "query": "boulangerie",
    "identifiers": [
        "652014051",
        "65201405100732",
        "477550370"
    ],
    "latitude": 48.8566,
    "longitude": 2.3522,
    "postalCodes": [
        "75011"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/france-company-search").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 = {
    "query": "boulangerie",
    "identifiers": [
        "652014051",
        "65201405100732",
        "477550370",
    ],
    "latitude": 48.8566,
    "longitude": 2.3522,
    "postalCodes": ["75011"],
}

# Run the Actor and wait for it to finish
run = client.actor("yadroo/france-company-search").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 '{
  "query": "boulangerie",
  "identifiers": [
    "652014051",
    "65201405100732",
    "477550370"
  ],
  "latitude": 48.8566,
  "longitude": 2.3522,
  "postalCodes": [
    "75011"
  ]
}' |
apify call yadroo/france-company-search --silent --output-dataset

```

## MCP server setup

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

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/F4oYsVbGkQS4ZCgNa/builds/tzOkjSx1oCsKRWORA/openapi.json
