# BODACC Scraper - French Insolvencies & Sales (`dataio/bodacc-french-insolvencies-sales`) Actor

Scrape BODACC French legal announcements as clean rows: insolvency proceedings with a normalized distress level, business sales with the price extracted from free text, and new company registrations with SIREN. Filter by event type, département and date. No API key.

- **URL**: https://apify.com/dataio/bodacc-french-insolvencies-sales.md
- **Developed by:** [Tom Awake](https://apify.com/dataio) (community)
- **Categories:** Lead generation, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 legal announcements

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

### What does BODACC Scraper do?

Every French legal announcement — insolvencies, business sales, company
creations — as usable rows instead of nested JSON.

French companies are legally required to publish their significant events
in the BODACC: a court opens a receivership, a business changes hands, a
company registers. **50.6 million announcements.** It is the official
record behind every French credit-risk score and every "company in
difficulty" alert.

It is published in a shape nobody can use: six nested JSON objects per row
(`listepersonnes`, `jugement`, `acte`, `listeetablissements`,
`listeprecedentproprietaire`, `depot`), free-text legal wording, and an API
that refuses to paginate past 10,000 results.

No API key. No account.

***

### The four things this does that the source does not

**1. It extracts the transaction price.**

The price of a business sale exists nowhere as a field. It is written out
in a sentence: *"établissement principal acquis par achat au prix stipulé
de 80000.00 euros"*. Parsed from free text on **85.5% of sales notices**
(measured on 800 recent notices). Against 891k sale notices, that is on the
order of 760,000 priced transactions — a comparables set for valuing French
businesses that does not otherwise exist in structured form.

Median price in the sample: €93,000. Largest: €79.7M.

The **seller** is recovered too, from a different nested object: name on
94% of priced notices, SIREN on 90%. So each row is a transaction with two
identified counterparties.

**2. It puts insolvencies on an ordered scale.**

The source says *"Jugement de conversion en liquidation judiciaire"*. An
analyst needs to know where the company sits and which way it is moving.

| `distressLevel` | Meaning |
|---|---|
| 1 | Safeguard — preventive, company still trading |
| 2 | Recovery or safeguard plan approved |
| 3 | Receivership opened, or plan resolved |
| 4 | Liquidation opened, or conversion to liquidation |
| 5 | Closed for insufficient assets; personal bankruptcy; director ban |

Plus `procedureType`, `isProcedureOpening` and `isProcedureClosure` — an
opening is a credit event, a closure is an epilogue, and they should never
be counted together.

Measured on 500 recent insolvency notices: Liquidation 342, Receivership
64, Sanctions 15, Safeguard 10.

Procedural filings — *filing of the list of claims*, *filing of the
distribution plan* — are labelled `Procedural filing` and deliberately
carry **no** distress level. They are administrative steps, not changes of
state, and giving them a level would invent a deterioration that did not
happen. They are 8.6% of insolvency notices, and `openingsOnly` removes
them.

**3. It computes the delay between judgement and publication.**

Median **9 days** — but up to **1,052 days** observed. A notice published
today can concern a judgement from three years ago. Without
`daysToPublication`, every monitoring system built on this source will
eventually report an old judgement as today's news.

**4. It gets past the 10,000-row ceiling.**

The public records API refuses `offset + limit > 10,000`, while a single
department holds 1.3 million notices. This Actor uses the export endpoint,
which does not have that limit, and always sorts by publication date —
without an explicit sort the API returns an arbitrary sample whose first
row was dated January 2025 rather than today.

***

### Field coverage

**`eventType` decides which columns fill.** The BODACC publishes seven
different kinds of notice, and each carries its own information: a sale
price exists only on a sale, share capital only where the registry
restates it, a court ruling only on an insolvency. This is not missing
data — it is a different kind of announcement.

Measured 17 September 2026 on 150 notices of each type:

| Field | `insolvencies` | `sales` | `creations` | `deregistrations` |
|---|---|---|---|---|
| `siren`, `companyName`, `court` | 96-100% | 97-100% | 100% | 100% |
| `department`, `region`, `publishedAt` | 100% | 100% | 100% | 100% |
| `street` / `city` / `postalCode` | 100% | 89% | 31% | 95% |
| `legalForm` | 93% | 89% | 31% | 59% |
| **`judgementDate`** | **100%** | — | — | — |
| **`procedureType`** | **99%** | — | — | — |
| **`eventNature`**, `eventDetail` | **100%** | — | — | — |
| **`distressLevel`** | **87%** | — | — | — |
| `daysSinceJudgement` | 100% | — | — | — |
| **`salePriceEur`**, `salePriceSource` | — | **86%** | — | — |
| **`sellerName`** / `sellerSiren` | — | **83 / 79%** | — | — |
| `capitalAmount` / `capitalCurrency` | — | 78% | 29% | 49% |
| `activityStartDate` | — | 77% | **96%** | — |
| `establishmentType`, `fundOrigin` | — | 86% | 82% | — |
| **`creationCategory`** | — | — | **100%** | — |
| `registrationDate` | — | 24% | **86%** | — |
| `activity` | 100% | — | — | 67% |
| `tradeName` | — | 11% | 38% | 19% |
| `previousOperatorName` / `Siren` | — | 5 / 4% | — | — |

A dash means the field is structurally absent for that notice type, not
that the extraction failed.

`previousOperatorName` is thin even on sales (5%): the outgoing operator
is named only when the registry chose to restate it.

There is no `country` column. Measured across 240 notices of all four
types, the BODACC publishes no country in its address blocks — only
`numeroVoie`, `typeVoie`, `nomVoie`, `complGeographique`, `codePostal`,
`ville` and `localite`. Hard-coding `FR` would be a constant carrying no
information, since this is by definition the bulletin of the French
commercial registries.

Companies and sole traders land in the same columns: `personType` says
which, and an individual's name is assembled into `companyName` rather
than left in separate fields.

***

### What it is for

- **Credit risk.** Filter `openingsOnly` on your customer or supplier
  SIRENs, run it daily, and you see a receivership the week it is
  pronounced.
- **Debt recovery.** `distressLevel` 4-5 tells you whether a claim is still
  worth pursuing.
- **M\&A and valuation.** 760k priced transactions with buyer, seller,
  activity and location. Comparables by sector and region.
- **B2B prospecting.** Company creations, the day they register, with
  address, capital, activity and SIREN.
- **Competitive monitoring.** Track a competitor's SIREN for every legal
  event it publishes.

Four dataset views ship with the Actor: **Notices**, **Insolvencies**,
**Transactions** and **New companies**.

***

### Running this as a scheduled watch

Insolvency notices are a credit-risk signal, and their value decays
fast: learning in March that a customer entered receivership in January
is learning too late.

```json
{
  "eventType": "insolvencies",
  "openingsOnly": true,
  "publishedWithinDays": 7,
  "maxItems": 500
}
```

Note the gap this Actor measures: the median delay between a court
ruling and its publication is 9 days, but reaches 839. `daysSince\
Judgement` tells you whether a fresh notice describes a fresh event.

**Why not a fixed `publishedFrom` date?** A schedule replays the same
input every time. A fixed date returns the same notices at every run and
charges for them again.

***

### Limits

Stated plainly, because they affect what you can conclude.

- **20,000 rows per run.** The export endpoint gets past the API's 10,000
  ceiling but is not unlimited. For a full historical extraction, split the
  work by date range.
- **Price extraction is a text parse, not a field.** 85.5% coverage means
  14.5% of sales carry no extractable price — often because there is
  genuinely none published (transfers by contribution, mergers, free-text
  notices). The monetary suffix is required deliberately: relaxing it would
  catch a few more, and would also read "Au capital de : 1 000 000,00" as a
  sale price. A missing price is honest; a wrong one corrupts the analysis.
- **One-euro sales are kept.** They are real — the symbolic takeover of an
  indebted company — and they are 2% of priced notices. Treat them as
  transfers of liability, not as valuations.
- **`distressLevel` is our normalisation, not an official scale.** It maps
  French judgement wording onto an ordered scale so rows can be sorted and
  compared. 5% of insolvency notices are not classified at all, mostly
  *"Autre jugement et ordonnance"*, which the source itself leaves
  undetermined. We leave them empty rather than guess.
- **Corrections are marked, not merged.** A notice can be a correction or a
  cancellation of an earlier one; `noticeType` and `isCorrection` say so.
  Reconciling a correction with its original is left to you, because the
  source does not reliably link them.
- **A published event is not a current state.** BODACC records events. A
  company with a receivership notice from 2023 may have recovered since.
  Use `daysSinceJudgement` and do not read a single notice as a status.
- **Sole traders are natural persons.** Their names appear because French
  law requires these announcements to be public. Their presence here
  carries the obligations that come with processing personal data.

***

### How much does it cost?

You pay per legal announcement returned: **$0.003 each**, that is $3.00 per 1,000. There is no start fee, and subscription plans pay less per legal announcement.

The example input below asks for up to 500 legal announcements, so it costs **$1.50 at most**.

If a run reaches the spending limit you set, the output stops at that limit and never goes past it. You are never charged for rows that were not delivered.

### Use BODACC Scraper as an API

Call it from your own code with the Apify client, here in Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("DataIO/bodacc-french-insolvencies-sales").call(run_input={
    'eventType': 'insolvencies',
    'openingsOnly': True,
    'maxItems': 500,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

It also works from JavaScript, Make, Zapier, n8n, and from AI agents through the Apify MCP server.

### Other actors you might like

- [GLEIF LEI Lookup](https://apify.com/DataIO/gleif-lei-lookup): Legal Entity Identifier API
- [SEC XBRL Screener](https://apify.com/DataIO/sec-edgar-xbrl-financial-screener): Company Financials Data
- [IRS 990 Scraper](https://apify.com/DataIO/irs-990-nonprofit-financials): Nonprofit Financials & Revenue

### FAQ

#### Is it legal to use this data?

The actor reads public data from its official source, without logging in and without bypassing any access control. What you do with the data, for example contacting people listed in it, is your responsibility under the laws that apply to you, such as GDPR in Europe.

#### Can I run it on a schedule?

Yes. See *Running this as a scheduled watch* above: use the rolling-window setting so each run returns only what is new.

#### Can AI agents use it?

Yes. It is available through the Apify MCP server, and every input field is described in its input schema, so an agent can call it directly.

### Source

[BODACC](https://www.bodacc.fr) (Bulletin officiel des annonces civiles et
commerciales), published by the DILA and distributed as open data through
opendatasoft. Public, no authentication. Licence Ouverte / Open Licence.
This Actor is not affiliated with the DILA.

# Actor input Schema

## `eventType` (type: `string`):

Insolvency proceedings (3.3M notices) are the credit-risk signal. Sales and transfers (891k) carry the transaction price. Creations (6.6M) are new companies, the day they register.

## `publishedWithinDays` (type: `integer`):

Rolling window, for a scheduled watch: 7 returns notices published in the last seven days. Insolvency notices are a credit-risk signal, so a weekly watch is the main use. Overrides Published from.

## `publishedFrom` (type: `string`):

YYYY-MM-DD. Leave empty to start from the most recent notices, which are returned first.

## `publishedTo` (type: `string`):

YYYY-MM-DD.

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

French department codes, for example 33, 75, 2A. Leave empty for the whole country.

## `siren` (type: `string`):

Track one company. The 9-digit SIREN, with or without spaces. Returns its complete notice history.

## `searchText` (type: `string`):

Searches the whole notice: company name, activity, town, judgement wording. Ignored when a SIREN is given.

## `openingsOnly` (type: `boolean`):

Keeps only the judgements that open a procedure — the actionable credit event. Excludes closures and procedural filings such as the filing of the list of claims, which are administrative steps rather than changes of state.

## `minDistressLevel` (type: `integer`):

1 safeguard, 2 recovery plan, 3 receivership, 4 liquidation, 5 closure for insufficient assets or director ban. Set 4 to keep only terminal outcomes.

## `minSalePriceEur` (type: `integer`):

Applies to sales and transfers. Set 1 to keep only notices where a price could be extracted — including the symbolic one-euro takeovers, which are real.

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

Most recent notices first. Capped at 20,000 per run; for larger extractions, split by date range.

## Actor input object example

```json
{
  "eventType": "insolvencies",
  "publishedWithinDays": 7,
  "openingsOnly": false,
  "maxItems": 1000
}
```

# Actor output Schema

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

Every row produced by the run, in JSON.

# 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 = {
    "publishedWithinDays": 7,
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataio/bodacc-french-insolvencies-sales").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 = {
    "publishedWithinDays": 7,
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("dataio/bodacc-french-insolvencies-sales").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 '{
  "publishedWithinDays": 7,
  "maxItems": 1000
}' |
apify call dataio/bodacc-french-insolvencies-sales --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataio/bodacc-french-insolvencies-sales"
        }
    }
}
```

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/B0wV36wdnABHK7lyf/builds/tLkYuJk1DlzQFUtjO/openapi.json
