# France Company Announcements (BODACC) (`shadow-tools/france-company-announcements-bodacc`) Actor

Search and monitor French BODACC company announcements: creations, modifications, sales, insolvency procedures, struck-off companies, account filings. Legal entities only (GDPR by design). Unofficial tool using official open data.

- **URL**: https://apify.com/shadow-tools/france-company-announcements-bodacc.md
- **Developed by:** [Shadow Tools](https://apify.com/shadow-tools) (community)
- **Categories:** Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 company notices

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## France Company Announcements (BODACC)

**Unofficial tool using the official open data of the BODACC** (Bulletin officiel des annonces civiles et commerciales, published by the DILA / Journal officiel). It searches and monitors the legal announcements about **French companies**: creations, registrations, modifications, sales of businesses, insolvency procedures (liquidation, receivership, safeguard), conciliation, struck-off companies and annual accounts filings.

**Call it when** you need to know what was officially published about a French company, a sector (company-name keywords), a department or region, or when you want a daily feed of new notices (monitor mode). **Legal entities only**: notices about natural persons are removed on purpose (see [Privacy / GDPR by design](#privacy--gdpr-by-design)).

- Source: BODACC open data, ~51 million notices since 2008, updated each publication day.
- Output: structured fields only (company, SIREN, legal form, seat, notice family/type, procedure type and date, link to the official notice). No free text.
- Price: pay per event, one event (`company-notice`) per notice delivered. You can cap the cost per run.

### Minimal input per mode

Search (also what an empty input does: the 20 most recent notices of the last 7 days):

```json
{ "mode": "search", "families": ["collective"], "procedureKeywords": ["liquidation"], "departments": ["33"], "sinceDays": 30, "maxResults": 20 }
```

Everything about known companies (any family, any period up to 10 years):

```json
{ "mode": "search", "sirens": ["552100554"], "sinceDays": 3650 }
```

Monitor (schedule it daily; returns only notices published since the previous run with the same filters):

```json
{ "mode": "monitor", "keywords": ["boulangerie"], "regions": ["Bretagne"], "maxResults": 100 }
```

#### Input fields

| Field | What it does | Example |
|---|---|---|
| `mode` | `search` = answer a question about a period; `monitor` = only what is new since the last run (for schedules) | `search` |
| `keywords` | Words in the company name (OR, start of word, accents/case ignored, French) | `["boulangerie"]` |
| `sirens` | 9-digit SIREN numbers (a 14-digit SIRET is cut) | `["552100554"]` |
| `families` | `creation`, `immatriculation`, `modification`, `vente`, `radiation`, `collective`, `conciliation`, `retablissement_professionnel`, `dpc` (accounts) | `["collective"]` |
| `noticeTypes` | `annonce`, `rectificatif` (correction), `annulation` | `["annonce"]` |
| `procedureKeywords` | Words in the judgment type label of insolvency notices | `["liquidation"]` |
| `departments` / `regions` | Department codes (01-95, 2A, 2B, 971-976) / official region names | `["75", "2A"]` / `["Bretagne"]` |
| `sinceDays` / `dateFrom` / `dateTo` | Period: last N days (default 7) or explicit `YYYY-MM-DD` dates | `30` / `2026-10-01` |
| `maxResults` | 1 to 10000, default 20 | `20` |

Invalid input stops the run with a message such as `Field departments invalid: expected a list of department codes (01-95, 2A, 2B, 971-976), got ["99"], example ["75", "2A", "974"].`

#### Monitor mode

The first run returns the most recent notices of the period (`sinceDays`). Next runs read forward from the last publication date reached and deliver each notice once; if `maxResults` or your cost cap stops a run early, the rest comes at the next run. The state is kept in your own account, per filter set; a different filter set starts its own history. The BODACC bulletins appear five times a week, so an empty run is normal.

### Output example (real, shortened)

```json
{
  "noticeId": "A202602133921",
  "publicationDate": "2026-11-05",
  "noticeFamily": "collective",
  "noticeFamilyLabel": "Procédures collectives",
  "noticeType": "annonce",
  "isCorrection": false,
  "court": "Greffe du Tribunal des Activités Economiques de Paris",
  "siren": "422205773",
  "denomination": "WOODS MEDIA GROUP",
  "legalForm": "Société à responsabilité limitée",
  "legalFormCode": "5499",
  "postcode": "75011",
  "municipality": "Paris",
  "departmentCode": "75",
  "region": "Île-de-France",
  "procedureNature": "Liste des créances nées après le jugement d'ouverture d'une procédure de liquidation judiciaire",
  "procedureDate": "2026-10-20",
  "noticeUrl": "https://www.bodacc.fr/pages/annonces-commerciales-detail/?q.id=id:A202602133921"
}
```

Other fields: `registryCity`, `activity` (declared activity, cut to 200 characters), `capitalAmount`/`capitalCurrency`, `department`, `procedureFamily`, `registrationDate`, `activityStartDate`, `creationCategory`, `accountsClosingDate`, `accountsFilingType`, `deregistrationReason`, `relatedCompanies` (SIREN + name of the other companies on the same notice), `attribution`, `extractedAt`.

**Corrections.** A notice of type `rectificatif` (correction) or `annulation` (cancellation) is delivered like any other and flagged: `isCorrection` / `isCancellation`, with `correctsNoticeId` and `correctsNoticeDate` pointing to the notice it corrects (checked live: the referenced notice exists). Use `noticeTypes: ["annonce"]` to leave them out.

The run also writes a `SUMMARY` record in the key-value store: window read, number returned, how many notices were read and removed by the privacy filter (by reason), company-directory statistics, and a plain-language `note` when the result is empty, partial or capped.

### Privacy / GDPR by design

**The filtering below is applied on purpose, to respect the GDPR.** This Actor returns no data about natural persons: no sole traders, no managers, liquidators, notaries, previous owners, no home addresses, phone numbers or e-mails. It is **fail-closed**: a notice is kept only if everything on it is clearly a legal entity; anything unknown, unreadable or ambiguous is dropped.

What is done, for every notice:

1. Every person listed on the notice (including previous owners and previous operators) must be typed as a legal entity (`pm`) by the source. One natural person (`pp`), a missing or unknown type, an unreadable list: the whole notice is dropped.
2. A "legal entity" carrying person-only data (surname, first name, birth data, sole-trader address, EIRL name) is a contradiction: dropped. Sole-trader legal forms (entrepreneur individuel...) are dropped.
3. Each entity needs a 9-digit SIREN and a name, and every registration number of the notice must belong to a listed legal entity.
4. **Cross-check against the public company directory** (`recherche-entreprises.api.gouv.fr`, no key, results cached): the SIREN must exist, must not be protected from diffusion, and its INSEE legal-form code must be a company/association/public-body code (3xxx-9xxx). Sole traders (1xxx) and groups without legal personality (2xxx) are dropped. If the directory is unreachable the Actor stops (partial) rather than returning unchecked records.
5. Only whitelisted structured fields are output. The free-text fields of the source (judgment details, managers/administration, sale deeds, previous owners, miscellaneous) are never copied, because they can name people. The declared activity of the company is scrubbed of e-mails, phone numbers and URLs and cut to 200 characters.

Measured on a real sample (5 Nov 2026, 14 days of notices, newest first, up to 160 per family, **1,129 notices**, 9 families): **898 kept (79.5 %), 231 dropped (20.5 %)**.

| Family | Read | Kept | Dropped |
|---|---|---|---|
| Creations | 160 | 28.8 % | 71.3 % (107 natural persons, 7 not yet in the directory) |
| Registrations | 160 | 73.1 % | 26.9 % |
| Modifications | 160 | 90.0 % | 10.0 % |
| Sales and transfers | 160 | 89.4 % | 10.6 % |
| Struck off | 160 | 90.0 % | 10.0 % |
| Insolvency procedures | 160 | 89.4 % | 10.6 % |
| Conciliation | 1 | 100 % | 0 % |
| Professional recovery | 8 | 0 % | 100 % |
| Accounts filings | 160 | 100 % | 0 % |

Reasons: natural person 221, not found in the directory 7 (very recent registrations are not in the directory yet), no valid SIREN 2, protected in the directory 1. Expect the share dropped to vary with the day and the family.

**What is not guaranteed.**

- A legal entity can be named after a person ("BOULANGERIE X"): the company name is company data and is returned as published. The Actor cannot know that a surname in a company name is someone's.
- The declared activity is text typed by the company; it is scrubbed for contact data but it is not checked for names.
- The classification relies on the source (`typePersonne`) and on the directory; an error in either would pass through. The data is dropped when they disagree, not when both are wrong the same way.
- Very recent registrations that are not yet in the directory are dropped (false negatives are accepted, false positives are what the design avoids).
- The DILA reminds re-users that publishing open data does not free the re-user from the French data protection law; you remain responsible for what you do with the output.

**Nothing about you is stored.** The Actor keeps no log of who asked what: no input is sent anywhere except the two public APIs (the BODACC filters and the SIREN numbers to verify). Your own account only holds the run data and two named stores (monitor cursor, and a cache of public directory answers: SIREN, status, legal-form code).

### For AI agents

Call this Actor to answer "what has been published about French company X / in sector Y / department Z" or to get new notices since last time. Minimal calls: `{}` (20 recent notices), `{"sirens":["552100554"],"sinceDays":3650}` (history of one company), `{"families":["collective"],"procedureKeywords":["liquidation"],"departments":["69"],"sinceDays":14}` (recent liquidations), `{"mode":"monitor"}` on a schedule. Keep `maxResults` small (default 20). Results are legal entities only; if you get 0 results read `SUMMARY.note` in the key-value store: it says whether nothing matches, everything was removed by the privacy filter, or the run was partial, and what to change. Each record has `noticeUrl`: quote the official notice when you answer.

### Data source, licence and credit

- BODACC open data on the DILA Opendatasoft instance: dataset `annonces-commerciales` (`bodacc-datadila.opendatasoft.com`, Explore API v2.1, no key). Licence shown on data.gouv.fr (page "BODACC", producer Premier ministre/DILA): **Licence Ouverte / Open Licence** (Etalab). The Opendatasoft page itself shows no licence.
- Company directory used for the legal-entity check: `recherche-entreprises.api.gouv.fr` (DINUM, no key, 7 requests per second per IP; its answers are read in memory and only the status and legal-form code are kept).
- Every record carries an `attribution` and `extractedAt`; the source and the update date must be credited when you reuse the data. This is an independent service, not affiliated with the DILA or the French government; check the official notice before acting. No warranty.

### Cost and limits

Pay per event: one `company-notice` event per delivered notice (plus the platform start event). Set "Maximum cost per run" to cap it: the Actor delivers as many notices as the cap allows and says so in the `SUMMARY`. The company-directory check limits the speed to about 5 notices per second, so 1,000 notices take 3 to 4 minutes. The run stops cleanly 40 seconds before the platform timeout and delivers what it has.

### FAQ

- **Is this the official API?** No. It reads the official open data and adds a legal-entity check. The official site is bodacc.fr.
- **Why are there fewer notices than on bodacc.fr?** Notices about natural persons and anything not verifiable as a legal entity are removed (about 20 % of recent notices, more for creations).
- **Does it cover all of France?** Metropolitan France and overseas departments, as published in the BODACC.
- **Freshness?** Same day as the publication on the Opendatasoft dataset (it was updated on 5 Nov 2026 for the 5 Nov bulletin).

### En français

Outil non officiel qui interroge les données ouvertes du BODACC (DILA) : créations, modifications, ventes, procédures collectives, radiations, dépôts de comptes des **personnes morales** uniquement. Par conception (RGPD), les annonces qui concernent une personne physique (entrepreneur individuel, dirigeant, liquidateur, ancien propriétaire...) sont écartées, et chaque SIREN est vérifié dans l'annuaire public des entreprises ; en cas de doute, l'annonce est écartée.

# Actor input Schema

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

Pick one. "search" = use for a question about announcements in a period (what was published about company X, which companies entered liquidation in Gironde this week): returns notices matching your filters, newest first. "monitor" = use only for scheduled (daily) runs: returns only notices published since the previous run with the same filters, state is kept per Actor and user store, and the first monitor run returns the most recent notices (sinceDays). Allowed values: search, monitor. Default: search.

## `keywords` (type: `array`):

Words searched in the company name (denomination), as a list, e.g. \["boulangerie", "transports"]. ANY of them matches (OR), at the start of a word, accents and case ignored. The company name is in French. Empty = no name filter.

## `sirens` (type: `array`):

Companies to follow, as a list of 9-digit SIREN numbers (a 14-digit SIRET is cut to its SIREN), e.g. \["552100554"]. Use it to get every announcement about known companies. Empty = no SIREN filter.

## `families` (type: `array`):

Kinds of announcement to keep, as a list, e.g. \["collective", "radiation"]. Allowed values: creation (Creations), immatriculation (Registrations), modification (Modifications), vente (Sales and transfers of businesses), radiation (Struck off (radiations)), collective (Insolvency (collective procedures)), conciliation (Conciliation procedures), retablissement_professionnel (Professional recovery procedures), dpc (Annual accounts filings). Empty = all families.

## `noticeTypes` (type: `array`):

Keep only some notice types, as a list, e.g. \["annonce"]. Allowed values: annonce (original notice), rectificatif (correction of an earlier notice: flagged isCorrection and linked in correctsNoticeId), annulation (cancellation of an earlier notice). Empty = all types, corrections included and flagged.

## `procedureKeywords` (type: `array`):

Words matched against the judgment type label of insolvency notices, as a list, e.g. \["liquidation", "redressement", "sauvegarde"]. ANY matches. Only notices with a judgment label can match, so combine with families \["collective"]. Empty = no procedure filter.

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

French department codes of the notice, as a list, e.g. \["75", "2A", "974"]. Codes are 01-95, 2A, 2B and 971-976 (overseas). Empty = all departments.

## `regions` (type: `array`):

Regions of the notice, as a list of official names, e.g. \["Bretagne", "Île-de-France"]. Empty = all regions.

## `sinceDays` (type: `integer`):

Look at notices published in the last N days, today included, as an integer, e.g. 30. Ignored in search mode when dateFrom is set; in monitor mode only used by the first run. Default: 7.

## `dateFrom` (type: `string`):

First publication date to include, format YYYY-MM-DD, e.g. 2026-10-01. Search mode only. Overrides sinceDays. Empty = use sinceDays.

## `dateTo` (type: `string`):

Last publication date to include, format YYYY-MM-DD, e.g. 2026-10-31. Search mode only. Empty = today (Paris time).

## `maxResults` (type: `integer`):

Maximum number of notices returned (and billed), as an integer from 1 to 10000, e.g. 20. Keep it small for a quick answer: a run of 1000 takes a few minutes. Default: 20.

## Actor input object example

```json
{
  "mode": "search",
  "keywords": [
    "boulangerie"
  ],
  "sirens": [
    "552100554"
  ],
  "families": [
    "collective"
  ],
  "noticeTypes": [
    "annonce"
  ],
  "procedureKeywords": [
    "liquidation"
  ],
  "departments": [
    "33",
    "75"
  ],
  "regions": [
    "Bretagne"
  ],
  "sinceDays": 7,
  "dateFrom": "2026-10-01",
  "dateTo": "2026-10-31",
  "maxResults": 20
}
```

# Actor output Schema

## `notices` (type: `string`):

Matching announcements with company name, SIREN, legal form, seat, procedure type and link to the official notice.

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

Every field of every notice, downloadable as JSON, CSV or Excel.

# 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("shadow-tools/france-company-announcements-bodacc").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("shadow-tools/france-company-announcements-bodacc").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 shadow-tools/france-company-announcements-bodacc --silent --output-dataset

```

## MCP server setup

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

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/pKcnEOEIvfbmOqbns/builds/lfEvWPsJcmfTfnLnc/openapi.json
